mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Move vfs.RestoreID to checkpoint.ResourceID.
... and use it everywhere in preference to its string. This is to be able to reliably inspect pgalloc.MemoryFile.ResourceID().Path in cl/879147587. PiperOrigin-RevId: 883274090
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(default_applicable_licenses = ["//:license"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "checkpoint",
|
||||
srcs = ["checkpoint.go"],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2026 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package checkpoint defines types that are used by multiple forms of gVisor
|
||||
// checkpoints.
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ResourceID is a unique ID that is used to identify resources between save/restore sessions.
|
||||
// Examples of resources are host files, gofer connection for mount points, etc.
|
||||
//
|
||||
// +stateify savable
|
||||
type ResourceID struct {
|
||||
// ContainerName is the name of the container that the resource belongs to.
|
||||
ContainerName string `json:"container_name"`
|
||||
// Path is the path of the resource. Path is never empty for a valid
|
||||
// ResourceID.
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// Ok returns true if the ResourceID is valid and not the zero value.
|
||||
func (id ResourceID) Ok() bool {
|
||||
return id.Path != ""
|
||||
}
|
||||
|
||||
func (id ResourceID) String() string {
|
||||
return fmt.Sprintf("%s:%s", id.ContainerName, id.Path)
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func Import(ctx context.Context, fdTable *kernel.FDTable, fds map[int]*fd.FD, op
|
||||
}
|
||||
var appFile *vfs.FileDescription
|
||||
|
||||
fdOpts.RestoreKey = host.MakeRestoreID(opts.ContainerName, appFD)
|
||||
fdOpts.RestoreKey = host.MakeResourceID(opts.ContainerName, appFD)
|
||||
if opts.Console && appFD < 3 {
|
||||
// Import the file as a host TTY file.
|
||||
if ttyFile == nil {
|
||||
|
||||
@@ -65,6 +65,7 @@ go_library(
|
||||
"//pkg/log",
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsutil",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/erofs"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
@@ -82,9 +83,9 @@ type filesystem struct {
|
||||
//
|
||||
// +stateify savable
|
||||
type InternalFilesystemOptions struct {
|
||||
// If UniqueID is non-empty, it is an opaque string used to reassociate the
|
||||
// filesystem with a new image FD during restoration from checkpoint.
|
||||
UniqueID vfs.RestoreID
|
||||
// If UniqueID is non-empty, it is used to reassociate the filesystem with
|
||||
// a new image FD during restoration from checkpoint.
|
||||
UniqueID checkpoint.ResourceID
|
||||
}
|
||||
|
||||
// Name implements vfs.FilesystemType.Name.
|
||||
|
||||
@@ -108,6 +108,7 @@ go_library(
|
||||
"//pkg/rand",
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/fsimpl/host",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
|
||||
@@ -58,6 +58,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/lisafs"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fsimpl/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -410,9 +411,9 @@ const (
|
||||
//
|
||||
// +stateify savable
|
||||
type InternalFilesystemOptions struct {
|
||||
// If UniqueID is non-empty, it is an opaque string used to reassociate the
|
||||
// filesystem with a new server FD during restoration from checkpoint.
|
||||
UniqueID vfs.RestoreID
|
||||
// If UniqueID is non-empty, it is used to reassociate the filesystem with
|
||||
// a new server FD during restoration from checkpoint.
|
||||
UniqueID checkpoint.ResourceID
|
||||
|
||||
// If LeakConnection is true, do not close the connection to the server
|
||||
// when the Filesystem is released. This is necessary for deployments in
|
||||
|
||||
@@ -42,6 +42,7 @@ go_library(
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsutil"
|
||||
@@ -117,7 +118,7 @@ type inode struct {
|
||||
hostFD int `state:"nosave"`
|
||||
|
||||
// restoreKey is used to identify the `hostFD` after a restore is performed.
|
||||
restoreKey vfs.RestoreID
|
||||
restoreKey checkpoint.ResourceID
|
||||
|
||||
// ino is an inode number unique within this filesystem.
|
||||
//
|
||||
@@ -194,7 +195,7 @@ type inode struct {
|
||||
termios linux.KernelTermios
|
||||
}
|
||||
|
||||
func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, restoreKey vfs.RestoreID, fileType linux.FileMode, isTTY bool, readonly bool) (*inode, error) {
|
||||
func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, restoreKey checkpoint.ResourceID, fileType linux.FileMode, isTTY bool, readonly bool) (*inode, error) {
|
||||
// Determine if hostFD is seekable.
|
||||
_, err := unix.Seek(hostFD, 0, linux.SEEK_CUR)
|
||||
seekable := !linuxerr.Equals(linuxerr.ESPIPE, err)
|
||||
@@ -249,7 +250,7 @@ type NewFDOptions struct {
|
||||
// RestoreKey is only used when Savable==true. It uniquely identifies the
|
||||
// host FD so that a mapping to the corresponding FD can be provided during
|
||||
// restore.
|
||||
RestoreKey vfs.RestoreID
|
||||
RestoreKey checkpoint.ResourceID
|
||||
|
||||
// Restorable is true if hostFD may be restored. This can be set to false
|
||||
// for host FDs that are not going to be present after restore.
|
||||
|
||||
@@ -24,16 +24,18 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/hostfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// MakeRestoreID creates a RestoreID for a given application FD. The application
|
||||
// FD remains the same between restores, e.g. stdout=2 before and after restore,
|
||||
// but the host FD that is maps to can change between restores. This ID is used
|
||||
// to map application FDs to their respective FD after a restore happens.
|
||||
func MakeRestoreID(containerName string, fd int) vfs.RestoreID {
|
||||
return vfs.RestoreID{
|
||||
// MakeResourceID creates a ResourceID for a given application FD. The
|
||||
// application FD remains the same between restores, e.g. stdout=2 before and
|
||||
// after restore, but the host FD that is maps to can change between restores.
|
||||
// This ID is used to map application FDs to their respective FD after a
|
||||
// restore happens.
|
||||
func MakeResourceID(containerName string, fd int) checkpoint.ResourceID {
|
||||
return checkpoint.ResourceID{
|
||||
ContainerName: containerName,
|
||||
Path: fmt.Sprintf("host:%d", fd),
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ go_library(
|
||||
"tar.go",
|
||||
"tmpfs.go",
|
||||
],
|
||||
imports = [
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
@@ -112,6 +115,7 @@ go_library(
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/fsutil",
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
@@ -26,16 +27,16 @@ import (
|
||||
var _ vfs.FilesystemImplSaveRestoreExtension = (*filesystem)(nil)
|
||||
|
||||
// saveMf is called by stateify.
|
||||
func (fs *filesystem) saveMf() string {
|
||||
func (fs *filesystem) saveMf() checkpoint.ResourceID {
|
||||
if !fs.mf.IsSavable() {
|
||||
panic(fmt.Sprintf("Can't save tmpfs filesystem because its MemoryFile is not savable: %v", fs.mf))
|
||||
}
|
||||
return fs.mf.RestoreID()
|
||||
return fs.mf.ResourceID()
|
||||
}
|
||||
|
||||
// loadMf is called by stateify.
|
||||
func (fs *filesystem) loadMf(ctx goContext.Context, restoreID string) {
|
||||
if restoreID == "" {
|
||||
func (fs *filesystem) loadMf(ctx goContext.Context, resourceID checkpoint.ResourceID) {
|
||||
if !resourceID.Ok() {
|
||||
fs.mf = pgalloc.MemoryFileFromContext(ctx)
|
||||
return
|
||||
}
|
||||
@@ -43,9 +44,9 @@ func (fs *filesystem) loadMf(ctx goContext.Context, restoreID string) {
|
||||
if mfmap == nil {
|
||||
panic("CtxMemoryFileMap was not provided")
|
||||
}
|
||||
mf, ok := mfmap[restoreID]
|
||||
mf, ok := mfmap[resourceID]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Memory file for %q not found in CtxMemoryFileMap", restoreID))
|
||||
panic(fmt.Sprintf("Memory file for %q not found in CtxMemoryFileMap", resourceID))
|
||||
}
|
||||
fs.mf = mf
|
||||
}
|
||||
@@ -62,18 +63,18 @@ func (d *dentry) loadParent(_ goContext.Context, parent *dentry) {
|
||||
|
||||
// PrepareSave implements vfs.FilesystemImplSaveRestoreExtension.PrepareSave.
|
||||
func (fs *filesystem) PrepareSave(ctx context.Context) error {
|
||||
restoreID := fs.mf.RestoreID()
|
||||
if restoreID == "" {
|
||||
resourceID := fs.mf.ResourceID()
|
||||
if !resourceID.Ok() {
|
||||
return nil
|
||||
}
|
||||
mfmap := pgalloc.MemoryFileMapFromContext(ctx)
|
||||
if mfmap == nil {
|
||||
return fmt.Errorf("CtxMemoryFileMap was not provided")
|
||||
}
|
||||
if _, ok := mfmap[restoreID]; ok {
|
||||
return fmt.Errorf("memory file for %q already exists in CtxMemoryFileMap", restoreID)
|
||||
if _, ok := mfmap[resourceID]; ok {
|
||||
return fmt.Errorf("memory file for %q already exists in CtxMemoryFileMap", resourceID)
|
||||
}
|
||||
mfmap[restoreID] = fs.mf
|
||||
mfmap[resourceID] = fs.mf
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ type filesystem struct {
|
||||
|
||||
// mf is used to allocate memory that stores regular file contents. mf is
|
||||
// immutable, except it is changed during restore.
|
||||
mf *pgalloc.MemoryFile `state:".(string)"`
|
||||
mf *pgalloc.MemoryFile `state:".(checkpoint.ResourceID)"`
|
||||
|
||||
// clock is a realtime clock used to set timestamps in file operations.
|
||||
clock ktime.Clock
|
||||
@@ -350,9 +350,9 @@ func (fs *filesystem) Release(ctx context.Context) {
|
||||
fs.ovlWhiteout.inode.decLinksLocked(ctx)
|
||||
}
|
||||
fs.mu.Unlock()
|
||||
if fs.mf.RestoreID() != "" {
|
||||
// If RestoreID is set, then this is a private MemoryFile which needs to be
|
||||
// destroyed since this tmpfs is the only user.
|
||||
if fs.mf.ResourceID().Ok() {
|
||||
// If ResourceID is set, then this is a private MemoryFile which needs
|
||||
// to be destroyed since this tmpfs is the only user.
|
||||
fs.mf.Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ go_library(
|
||||
"//pkg/safemem",
|
||||
"//pkg/secio",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/devices/nvproxy/nvconf",
|
||||
"//pkg/sentry/fdcollector",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
|
||||
@@ -55,6 +55,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
|
||||
@@ -597,10 +598,10 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
|
||||
// +stateify savable
|
||||
type privateMemoryFileMetadata struct {
|
||||
owners []string
|
||||
owners []checkpoint.ResourceID
|
||||
}
|
||||
|
||||
func savePrivateMFs(ctx context.Context, w io.Writer, mfsToSave map[string]*pgalloc.MemoryFile, mfOpts *pgalloc.SaveOpts) error {
|
||||
func savePrivateMFs(ctx context.Context, w io.Writer, mfsToSave map[checkpoint.ResourceID]*pgalloc.MemoryFile, mfOpts *pgalloc.SaveOpts) error {
|
||||
var meta privateMemoryFileMetadata
|
||||
// Generate the order in which private memory files are saved.
|
||||
for fsID := range mfsToSave {
|
||||
@@ -664,7 +665,7 @@ func (k *Kernel) SaveTo(ctx context.Context, stateFile, pagesMetadata io.WriteCl
|
||||
}
|
||||
|
||||
// Capture all private memory files.
|
||||
mfsToSave := make(map[string]*pgalloc.MemoryFile)
|
||||
mfsToSave := make(map[checkpoint.ResourceID]*pgalloc.MemoryFile)
|
||||
vfsCtx := context.WithValue(ctx, pgalloc.CtxMemoryFileMap, mfsToSave)
|
||||
// Prepare filesystems for saving. This must be done after
|
||||
// invalidateUnsavableMappings(), since dropping memory mappings may
|
||||
@@ -769,7 +770,7 @@ func (k *Kernel) BeforeResume(ctx context.Context) {
|
||||
// pagesFile must be non-nil, saveMemoryFiles takes ownership of both
|
||||
// pagesMetadata and pagesFile (even if it returns a non-nil error), and
|
||||
// MemoryFile state will be saved to pagesMetadata and pagesFile.
|
||||
func (k *Kernel) saveMemoryFiles(ctx context.Context, w io.Writer, pagesMetadata io.WriteCloser, pagesFile stateio.AsyncWriter, mfsToSave map[string]*pgalloc.MemoryFile, appMFExcludeCommittedZeroPages bool) error {
|
||||
func (k *Kernel) saveMemoryFiles(ctx context.Context, w io.Writer, pagesMetadata io.WriteCloser, pagesFile stateio.AsyncWriter, mfsToSave map[checkpoint.ResourceID]*pgalloc.MemoryFile, appMFExcludeCommittedZeroPages bool) error {
|
||||
memoryStart := time.Now()
|
||||
|
||||
pmw := w
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/state/stateio"
|
||||
"gvisor.dev/gvisor/pkg/state"
|
||||
@@ -210,7 +211,7 @@ func (w *CheckpointWaitable) signal(gen CheckpointGeneration, err error) {
|
||||
// loadPrivateMemoryFiles loads the private MemoryFiles from mfmap and it reads
|
||||
// private MemoryFile metadata from `r`. This consumes bytes from `r`, so this
|
||||
// must be called only once.
|
||||
func loadPrivateMemoryFiles(ctx context.Context, r io.Reader, mfmap map[string]*pgalloc.MemoryFile, opts *pgalloc.LoadOpts) error {
|
||||
func loadPrivateMemoryFiles(ctx context.Context, r io.Reader, mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile, opts *pgalloc.LoadOpts) error {
|
||||
// Load the metadata.
|
||||
var meta privateMemoryFileMetadata
|
||||
if _, err := state.Load(ctx, r, &meta); err != nil {
|
||||
@@ -258,7 +259,7 @@ func (k *Kernel) loadMemoryFiles(ctx context.Context, r io.Reader) error {
|
||||
type AsyncMFLoader struct {
|
||||
// privateMFsChan is used to tell the background goroutine about private
|
||||
// MemoryFiles, once they are known. This channel is written to exactly once.
|
||||
privateMFsChan chan map[string]*pgalloc.MemoryFile
|
||||
privateMFsChan chan map[checkpoint.ResourceID]*pgalloc.MemoryFile
|
||||
|
||||
mainMFStartWg sync.WaitGroup
|
||||
mainMetadataErr error
|
||||
@@ -279,7 +280,7 @@ type AsyncMFLoader struct {
|
||||
// pages.
|
||||
func NewAsyncMFLoader(pagesMetadata io.ReadCloser, pagesFile stateio.AsyncReader, mainMF *pgalloc.MemoryFile, timeline *timing.Timeline) *AsyncMFLoader {
|
||||
mfl := &AsyncMFLoader{
|
||||
privateMFsChan: make(chan map[string]*pgalloc.MemoryFile, 1),
|
||||
privateMFsChan: make(chan map[checkpoint.ResourceID]*pgalloc.MemoryFile, 1),
|
||||
}
|
||||
mfl.mainMFStartWg.Add(1)
|
||||
mfl.metadataWg.Add(1)
|
||||
@@ -351,7 +352,7 @@ func (mfl *AsyncMFLoader) backgroundGoroutine(pagesMetadata io.ReadCloser, pages
|
||||
}
|
||||
|
||||
// KickoffPrivate notifies the background goroutine of the private MemoryFiles.
|
||||
func (mfl *AsyncMFLoader) KickoffPrivate(mfmap map[string]*pgalloc.MemoryFile) {
|
||||
func (mfl *AsyncMFLoader) KickoffPrivate(mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile) {
|
||||
mfl.privateMFsChan <- mfmap
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,9 @@ go_library(
|
||||
"vma.go",
|
||||
"vma_set.go",
|
||||
],
|
||||
imports = [
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
@@ -159,6 +162,7 @@ go_library(
|
||||
"//pkg/safecopy",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/futex",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
|
||||
+1
-1
@@ -330,7 +330,7 @@ type pma struct {
|
||||
// file is the file mapped by this pma. Only pmas for which file is of type
|
||||
// pgalloc.MemoryFile may be saved. pmas hold a reference to the
|
||||
// corresponding file range while they exist.
|
||||
file memmap.File `state:".(string)"`
|
||||
file memmap.File `state:".(checkpoint.ResourceID)"`
|
||||
|
||||
// off is the offset into file at which this pma begins.
|
||||
off uint64
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
)
|
||||
|
||||
@@ -150,7 +151,7 @@ func (v *vma) loadRealPerms(_ goContext.Context, b int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pma) saveFile() string {
|
||||
func (p *pma) saveFile() checkpoint.ResourceID {
|
||||
mf, ok := p.file.(*pgalloc.MemoryFile)
|
||||
if !ok {
|
||||
// InvalidateUnsavable should have caused all such pmas to be
|
||||
@@ -160,18 +161,18 @@ func (p *pma) saveFile() string {
|
||||
if !mf.IsSavable() {
|
||||
panic(fmt.Sprintf("Can't save pma because its MemoryFile is not savable: %v", mf))
|
||||
}
|
||||
return mf.RestoreID()
|
||||
return mf.ResourceID()
|
||||
}
|
||||
|
||||
func (p *pma) loadFile(ctx goContext.Context, restoreID string) {
|
||||
if restoreID == "" {
|
||||
func (p *pma) loadFile(ctx goContext.Context, resourceID checkpoint.ResourceID) {
|
||||
if !resourceID.Ok() {
|
||||
p.file = pgalloc.MemoryFileFromContext(ctx)
|
||||
return
|
||||
}
|
||||
mfmap := pgalloc.MemoryFileMapFromContext(ctx)
|
||||
mf, ok := mfmap[restoreID]
|
||||
mf, ok := mfmap[resourceID]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("can't restore pma because its MemoryFile's restore ID %q was not found in CtxMemoryFileMap", restoreID))
|
||||
panic(fmt.Sprintf("can't restore pma because its MemoryFile's resource ID %q was not found in CtxMemoryFileMap", resourceID))
|
||||
}
|
||||
p.file = mf
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ go_library(
|
||||
"//pkg/ringdeque",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/hostmm",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/state/stateio",
|
||||
|
||||
@@ -16,6 +16,8 @@ package pgalloc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
)
|
||||
|
||||
// contextID is this package's type for context.Context.Value keys.
|
||||
@@ -53,9 +55,9 @@ func MemoryCgroupIDFromContext(ctx context.Context) uint32 {
|
||||
|
||||
// MemoryFileMapFromContext returns the memory file map used by ctx, or nil if
|
||||
// no such map exists.
|
||||
func MemoryFileMapFromContext(ctx context.Context) map[string]*MemoryFile {
|
||||
func MemoryFileMapFromContext(ctx context.Context) map[checkpoint.ResourceID]*MemoryFile {
|
||||
if v := ctx.Value(CtxMemoryFileMap); v != nil {
|
||||
return v.(map[string]*MemoryFile)
|
||||
return v.(map[checkpoint.ResourceID]*MemoryFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/hostmm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usage"
|
||||
@@ -361,9 +362,9 @@ type MemoryFileOpts struct {
|
||||
// DiskBackedFile indicates that the MemoryFile is backed by a file on disk.
|
||||
DiskBackedFile bool
|
||||
|
||||
// RestoreID is an opaque string used to reassociate the MemoryFile with its
|
||||
// replacement during restore.
|
||||
RestoreID string
|
||||
// ResourceID is used to reassociate the MemoryFile with its replacement
|
||||
// during restore.
|
||||
ResourceID checkpoint.ResourceID
|
||||
|
||||
// If ExpectHugepages is true, MemoryFile will expect that the host will
|
||||
// attempt to back AllocOpts.Huge == true allocations with huge pages. If
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/ringdeque"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/state/stateio"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usage"
|
||||
@@ -57,9 +58,9 @@ func (f *MemoryFile) IsSavable() bool {
|
||||
return f.savable
|
||||
}
|
||||
|
||||
// RestoreID returns the restore ID for f.
|
||||
func (f *MemoryFile) RestoreID() string {
|
||||
return f.opts.RestoreID
|
||||
// ResourceID returns the resource ID for f.
|
||||
func (f *MemoryFile) ResourceID() checkpoint.ResourceID {
|
||||
return f.opts.ResourceID
|
||||
}
|
||||
|
||||
// memoryFileSaved is the subset of MemoryFile that is stored in checkpoints.
|
||||
|
||||
@@ -189,6 +189,7 @@ go_library(
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/fsimpl/lock",
|
||||
"//pkg/sentry/fsmetric",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
goContext "context"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
)
|
||||
|
||||
// contextID is this package's type for context.Context.Value keys.
|
||||
@@ -30,9 +31,9 @@ const (
|
||||
// CtxRoot is a Context.Value key for a VFS root.
|
||||
CtxRoot
|
||||
|
||||
// CtxRestoreFilesystemFDMap is a Context.Value key for a map[string]int
|
||||
// mapping filesystem unique IDs (cf. gofer.InternalFilesystemOptions.UniqueID)
|
||||
// to host FDs.
|
||||
// CtxRestoreFilesystemFDMap is a Context.Value key for a
|
||||
// map[checkpoint.ResourceID]int mapping filesystem unique IDs (cf.
|
||||
// gofer.InternalFilesystemOptions.UniqueID) to host FDs.
|
||||
CtxRestoreFilesystemFDMap
|
||||
)
|
||||
|
||||
@@ -49,8 +50,8 @@ func MountNamespaceFromContext(ctx goContext.Context) *MountNamespace {
|
||||
|
||||
// RestoreFilesystemFDMapFromContext returns the RestoreFilesystemFDMap used
|
||||
// by ctx. If ctx is not associated with a RestoreFilesystemFDMap, returns nil.
|
||||
func RestoreFilesystemFDMapFromContext(ctx goContext.Context) map[RestoreID]int {
|
||||
fdmap, ok := ctx.Value(CtxRestoreFilesystemFDMap).(map[RestoreID]int)
|
||||
func RestoreFilesystemFDMapFromContext(ctx goContext.Context) map[checkpoint.ResourceID]int {
|
||||
fdmap, ok := ctx.Value(CtxRestoreFilesystemFDMap).(map[checkpoint.ResourceID]int)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -179,18 +179,3 @@ func (epi *epollInterest) afterLoad(goContext.Context) {
|
||||
// EpollInstance.ReadEvents() rechecks their readiness.
|
||||
epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask))
|
||||
}
|
||||
|
||||
// RestoreID is a unique ID that is used to identify resources between save/restore sessions.
|
||||
// Example of resources are host files, gofer connection for mount points, etc.
|
||||
//
|
||||
// +stateify savable
|
||||
type RestoreID struct {
|
||||
// ContainerName is the name of the container that the resource belongs to.
|
||||
ContainerName string
|
||||
// Path is the path of the resource.
|
||||
Path string
|
||||
}
|
||||
|
||||
func (f RestoreID) String() string {
|
||||
return fmt.Sprintf("%s:%s", f.ContainerName, f.Path)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ go_library(
|
||||
"//pkg/refs",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/arch:registers_go_proto",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/control",
|
||||
"//pkg/sentry/devices/memdev",
|
||||
"//pkg/sentry/devices/nvproxy",
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/devutil"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
|
||||
@@ -241,8 +242,8 @@ func (r *restorer) restore(l *Loader) error {
|
||||
})
|
||||
defer cu.Clean()
|
||||
|
||||
fdmap := make(map[vfs.RestoreID]int)
|
||||
mfmap := make(map[string]*pgalloc.MemoryFile)
|
||||
fdmap := make(map[checkpoint.ResourceID]int)
|
||||
mfmap := make(map[checkpoint.ResourceID]*pgalloc.MemoryFile)
|
||||
for _, cont := range r.containers {
|
||||
// TODO(b/298078576): Need to process hints here probably
|
||||
mntr := newContainerMounter(cont, l.k, l.mountHints, l.sharedMounts, l.productName, cont.cid)
|
||||
@@ -251,11 +252,11 @@ func (r *restorer) restore(l *Loader) error {
|
||||
}
|
||||
|
||||
for i, fd := range cont.stdioFDs {
|
||||
key := host.MakeRestoreID(cont.containerName, i)
|
||||
key := host.MakeResourceID(cont.containerName, i)
|
||||
fdmap[key] = fd.Release()
|
||||
}
|
||||
for _, customFD := range cont.passFDs {
|
||||
key := host.MakeRestoreID(cont.containerName, customFD.guest)
|
||||
key := host.MakeResourceID(cont.containerName, customFD.guest)
|
||||
fdmap[key] = customFD.host.FD()
|
||||
}
|
||||
}
|
||||
|
||||
+15
-14
@@ -36,6 +36,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/checkpoint"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/memdev"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy/nvconf"
|
||||
@@ -511,7 +512,7 @@ func (c *containerMounter) createMountNamespace(ctx context.Context, conf *confi
|
||||
InternalMount: true,
|
||||
Data: strings.Join(data, ","),
|
||||
InternalData: gofer.InternalFilesystemOptions{
|
||||
UniqueID: vfs.RestoreID{
|
||||
UniqueID: checkpoint.ResourceID{
|
||||
ContainerName: c.containerName,
|
||||
Path: "/",
|
||||
},
|
||||
@@ -527,7 +528,7 @@ func (c *containerMounter) createMountNamespace(ctx context.Context, conf *confi
|
||||
InternalMount: true,
|
||||
Data: fmt.Sprintf("ifd=%d", ioFD),
|
||||
InternalData: erofs.InternalFilesystemOptions{
|
||||
UniqueID: vfs.RestoreID{
|
||||
UniqueID: checkpoint.ResourceID{
|
||||
ContainerName: c.containerName,
|
||||
Path: "/",
|
||||
},
|
||||
@@ -647,7 +648,7 @@ func (c *containerMounter) configureOverlay(ctx context.Context, conf *config.Co
|
||||
}
|
||||
if filestoreFD != nil {
|
||||
// Create memory file for disk-backed overlays.
|
||||
mf, err := createPrivateMemoryFile(filestoreFD.ReleaseToFile("overlay-filestore"), vfs.RestoreID{ContainerName: c.containerName, Path: dst})
|
||||
mf, err := createPrivateMemoryFile(filestoreFD.ReleaseToFile("overlay-filestore"), checkpoint.ResourceID{ContainerName: c.containerName, Path: dst})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create memory file for overlay: %v", err)
|
||||
}
|
||||
@@ -950,7 +951,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
|
||||
return "", nil, err
|
||||
}
|
||||
if m.filestoreFD != nil {
|
||||
mf, err := createPrivateMemoryFile(m.filestoreFD.ReleaseToFile("tmpfs-filestore"), vfs.RestoreID{ContainerName: containerName, Path: m.mount.Destination})
|
||||
mf, err := createPrivateMemoryFile(m.filestoreFD.ReleaseToFile("tmpfs-filestore"), checkpoint.ResourceID{ContainerName: containerName, Path: m.mount.Destination})
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to create memory file for tmpfs: %v", err)
|
||||
}
|
||||
@@ -975,7 +976,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
|
||||
}
|
||||
data = append(data, goferMountData(m.goferFD.Release(), getMountAccessType(conf, m.hint), conf)...)
|
||||
internalData = gofer.InternalFilesystemOptions{
|
||||
UniqueID: vfs.RestoreID{
|
||||
UniqueID: checkpoint.ResourceID{
|
||||
ContainerName: containerName,
|
||||
Path: m.mount.Destination,
|
||||
},
|
||||
@@ -994,7 +995,7 @@ func getMountNameAndOptions(spec *specs.Spec, conf *config.Config, m *mountInfo,
|
||||
}
|
||||
data = []string{fmt.Sprintf("ifd=%d", m.goferFD.Release())}
|
||||
internalData = erofs.InternalFilesystemOptions{
|
||||
UniqueID: vfs.RestoreID{
|
||||
UniqueID: checkpoint.ResourceID{
|
||||
ContainerName: containerName,
|
||||
Path: m.mount.Destination,
|
||||
},
|
||||
@@ -1052,7 +1053,7 @@ func parseKeyValue(s string) (string, string, bool) {
|
||||
return strings.TrimSpace(tokens[0]), strings.TrimSpace(tokens[1]), true
|
||||
}
|
||||
|
||||
func createPrivateMemoryFile(file *os.File, restoreID vfs.RestoreID) (*pgalloc.MemoryFile, error) {
|
||||
func createPrivateMemoryFile(file *os.File, resourceID checkpoint.ResourceID) (*pgalloc.MemoryFile, error) {
|
||||
mfOpts := pgalloc.MemoryFileOpts{
|
||||
// Private memory files are usually backed by files on disk. Ideally we
|
||||
// would confirm with fstatfs(2) but that is prohibited by seccomp.
|
||||
@@ -1064,7 +1065,7 @@ func createPrivateMemoryFile(file *os.File, restoreID vfs.RestoreID) (*pgalloc.M
|
||||
// are expected to have performed the work around outside the sandbox.
|
||||
DisableIMAWorkAround: true,
|
||||
// Private memory files need to be restored correctly using this ID.
|
||||
RestoreID: restoreID.String(),
|
||||
ResourceID: resourceID,
|
||||
}
|
||||
return pgalloc.NewMemoryFile(file, mfOpts)
|
||||
}
|
||||
@@ -1392,10 +1393,10 @@ func (c *containerMounter) makeMountPoint(
|
||||
|
||||
// configureRestore returns an updated context.Context including filesystem
|
||||
// state used by restore defined by conf.
|
||||
func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap map[string]*pgalloc.MemoryFile) error {
|
||||
func (c *containerMounter) configureRestore(fdmap map[checkpoint.ResourceID]int, mfmap map[checkpoint.ResourceID]*pgalloc.MemoryFile) error {
|
||||
// Compare createMountNamespace(); rootfs always consumes a gofer FD and a
|
||||
// filestore FD is consumed if the rootfs GoferMountConf indicates so.
|
||||
rootKey := vfs.RestoreID{ContainerName: c.containerName, Path: "/"}
|
||||
rootKey := checkpoint.ResourceID{ContainerName: c.containerName, Path: "/"}
|
||||
fdmap[rootKey] = c.goferFDs.remove()
|
||||
|
||||
if rootfsConf := c.goferMountConfs[0]; rootfsConf.IsFilestorePresent() {
|
||||
@@ -1403,7 +1404,7 @@ func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap m
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create private memory file for mount rootfs: %w", err)
|
||||
}
|
||||
mfmap[rootKey.String()] = mf
|
||||
mfmap[rootKey] = mf
|
||||
}
|
||||
// prepareMounts() consumes the remaining FDs for submounts.
|
||||
mounts, err := c.prepareMounts()
|
||||
@@ -1413,16 +1414,16 @@ func (c *containerMounter) configureRestore(fdmap map[vfs.RestoreID]int, mfmap m
|
||||
for i := range mounts {
|
||||
submount := &mounts[i]
|
||||
if submount.goferFD != nil {
|
||||
key := vfs.RestoreID{ContainerName: c.containerName, Path: submount.mount.Destination}
|
||||
key := checkpoint.ResourceID{ContainerName: c.containerName, Path: submount.mount.Destination}
|
||||
fdmap[key] = submount.goferFD.Release()
|
||||
}
|
||||
if submount.filestoreFD != nil {
|
||||
key := vfs.RestoreID{ContainerName: c.containerName, Path: submount.mount.Destination}
|
||||
key := checkpoint.ResourceID{ContainerName: c.containerName, Path: submount.mount.Destination}
|
||||
mf, err := createPrivateMemoryFile(submount.filestoreFD.ReleaseToFile("overlay-filestore"), key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create private memory file for mount %q: %w", submount.mount.Destination, err)
|
||||
}
|
||||
mfmap[key.String()] = mf
|
||||
mfmap[key] = mf
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user