Skip to content

Add the checkpoint/restore support to EROFS #9577

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions pkg/erofs/erofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,25 @@ func OpenImage(src *os.File) (*Image, error) {
return i, nil
}

// UpdateImage updates the underlying image file. This is typically used in checkpoint/restore.
//
// On success, the ownership of src is transferred to Image.
//
// Preconditions:
// - i.src == nil.
// - i.bytes == nil.
func (i *Image) UpdateImage(src *os.File) error {
newImage, err := OpenImage(src)
if err != nil {
return err
}
if newImage.sb != i.sb {
return fmt.Errorf("superblock mismatch detected, got %+v, expected %+v", newImage.sb, i.sb)
}
*i = *newImage
return nil
}

// Close closes the image.
func (i *Image) Close() {
unix.Munmap(i.bytes)
Expand Down
27 changes: 27 additions & 0 deletions pkg/sentry/fsimpl/erofs/erofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"gvisor.dev/gvisor/pkg/erofs"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)

Expand All @@ -51,6 +52,7 @@ type filesystem struct {

// Immutable options.
mopts string
iopts InternalFilesystemOptions

// devMinor is the filesystem's minor device number. devMinor is immutable.
devMinor uint32
Expand All @@ -70,6 +72,16 @@ type filesystem struct {
inodeBuckets []inodeBucket
}

// InternalFilesystemOptions may be passed as
// vfs.GetFilesystemOptions.InternalData to FilesystemType.GetFilesystem.
//
// +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 string
}

// Name implements vfs.FilesystemType.Name.
func (FilesystemType) Name() string {
return Name
Expand Down Expand Up @@ -98,13 +110,20 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
}
cu.Add(func() { image.Close() })

iopts, ok := opts.InternalData.(InternalFilesystemOptions)
if opts.InternalData != nil && !ok {
ctx.Warningf("erofs.FilesystemType.GetFilesystem: GetFilesystemOptions.InternalData has type %T, wanted erofs.InternalFilesystemOptions", opts.InternalData)
return nil, nil, linuxerr.EINVAL
}

devMinor, err := vfsObj.GetAnonBlockDevMinor()
if err != nil {
return nil, nil, err
}

fs := &filesystem{
mopts: opts.Data,
iopts: iopts,
image: image,
devMinor: devMinor,
mf: imageMemmapFile{image: image},
Expand Down Expand Up @@ -243,6 +262,14 @@ type inode struct {
// +checklocks:dirMu
dirents []vfs.Dirent `state:"nosave"`

// mapsMu protects mappings.
mapsMu sync.Mutex `state:"nosave"`

// mappings tracks the mappings of the file into memmap.MappingSpaces
// if this inode represents a regular file.
// +checklocks:mapsMu
mappings memmap.MappingSet

// locks supports POSIX and BSD style locks.
locks vfs.FileLocks

Expand Down
10 changes: 10 additions & 0 deletions pkg/sentry/fsimpl/erofs/regular_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,22 @@ func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpt

// AddMapping implements memmap.Mappable.AddMapping.
func (i *inode) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
i.mapsMu.Lock()
i.mappings.AddMapping(ms, ar, offset, writable)
i.mapsMu.Unlock()
return nil
}

// RemoveMapping implements memmap.Mappable.RemoveMapping.
func (i *inode) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
i.mapsMu.Lock()
i.mappings.RemoveMapping(ms, ar, offset, writable)
i.mapsMu.Unlock()
}

// CopyMapping implements memmap.Mappable.CopyMapping.
func (i *inode) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
i.AddMapping(ctx, ms, dstAR, offset, writable)
return nil
}

Expand Down Expand Up @@ -175,6 +182,9 @@ func (i *inode) Translate(ctx context.Context, required, optional memmap.Mappabl

// InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
func (i *inode) InvalidateUnsavable(ctx context.Context) error {
i.mapsMu.Lock()
i.mappings.InvalidateAll(memmap.InvalidateOpts{})
i.mapsMu.Unlock()
return nil
}

Expand Down
31 changes: 30 additions & 1 deletion pkg/sentry/fsimpl/erofs/save_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,36 @@

package erofs

// TODO: support checkpoint/restore.
import (
"fmt"
"os"

"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)

// Compile-time assertion that filesystem implements vfs.FilesystemImplSaveRestoreExtension.
var _ = vfs.FilesystemImplSaveRestoreExtension((*filesystem)(nil))

// PreprareSave implements vfs.FilesystemImplSaveRestoreExtension.PrepareSave.
func (fs *filesystem) PrepareSave(ctx context.Context) error {
return nil
}

// CompleteRestore implements
// vfs.FilesystemImplSaveRestoreExtension.CompleteRestore.
func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRestoreOptions) error {
fdmapv := ctx.Value(vfs.CtxRestoreFilesystemFDMap)
if fdmapv == nil {
return fmt.Errorf("no image FD map available")
}
fdmap := fdmapv.(map[string]int)
fd, ok := fdmap[fs.iopts.UniqueID]
if !ok {
return fmt.Errorf("no image FD available for filesystem with unique ID %q", fs.iopts.UniqueID)
}
return fs.image.UpdateImage(os.NewFile(uintptr(fd), "EROFS image file"))
}

// saveParent is called by stateify.
func (d *dentry) saveParent() *dentry {
Expand Down
11 changes: 1 addition & 10 deletions pkg/sentry/fsimpl/gofer/save_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/vfs"
)

type saveRestoreContextID int

const (
// CtxRestoreServerFDMap is a Context.Value key for a map[string]int
// mapping filesystem unique IDs (cf. InternalFilesystemOptions.UniqueID)
// to host FDs.
CtxRestoreServerFDMap saveRestoreContextID = iota
)

// +stateify savable
type savedDentryRW struct {
read bool
Expand Down Expand Up @@ -181,7 +172,7 @@ func (d *dentry) loadParent(parent *dentry) {
// CompleteRestore implements
// vfs.FilesystemImplSaveRestoreExtension.CompleteRestore.
func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRestoreOptions) error {
fdmapv := ctx.Value(CtxRestoreServerFDMap)
fdmapv := ctx.Value(vfs.CtxRestoreFilesystemFDMap)
if fdmapv == nil {
return fmt.Errorf("no server FD map available")
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/vfs/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ 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
)

// MountNamespaceFromContext returns the MountNamespace used by ctx. If ctx is
Expand Down
2 changes: 1 addition & 1 deletion runsc/boot/vfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ func (c *containerMounter) configureRestore(ctx context.Context) (context.Contex
fdmap[submount.mount.Destination] = submount.goferFD.Release()
}
}
return context.WithValue(ctx, gofer.CtxRestoreServerFDMap, fdmap), nil
return context.WithValue(ctx, vfs.CtxRestoreFilesystemFDMap, fdmap), nil
}

func createDeviceFiles(ctx context.Context, creds *auth.Credentials, info *containerInfo, vfsObj *vfs.VirtualFilesystem, root vfs.VirtualDentry) error {
Expand Down