erofs: add checkpoint/restore support

Updates #8956

Signed-off-by: Tiwei Bie <tiwei.btw@antgroup.com>
This commit is contained in:
Tiwei Bie
2023-10-23 22:02:41 +08:00
parent 396d0beb1d
commit 78a667cb8a
4 changed files with 86 additions and 1 deletions
+19
View File
@@ -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)
+27
View File
@@ -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"
)
@@ -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
@@ -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
@@ -98,6 +110,12 @@ 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
@@ -105,6 +123,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
fs := &filesystem{
mopts: opts.Data,
iopts: iopts,
image: image,
devMinor: devMinor,
mf: imageMemmapFile{image: image},
@@ -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
+10
View File
@@ -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
}
@@ -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
}
+30 -1
View File
@@ -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 {