mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
fs: Add new cache policy "remote_revalidate".
This CL adds a new cache-policy for gofer filesystems that uses the host page cache, but causes dirents to be reloaded on each Walk, and does not cache readdir results. This policy is useful when the remote filesystem may change out from underneath us, as any remote changes will be reflected on the next Walk. Importantly, this cache policy is only consistent if we do not use gVisor's internal page cache, since that page cache is tied to the Inode and may be thrown away upon Revalidation. This cache policy should only be used when the gofer supports donating host FDs, since then gVisor will make use of the host kernel page cache, which will be consistent for all open files in the gofer. In fact, a panic will be raised if a file is opened without a donated FD. PiperOrigin-RevId: 207752937 Change-Id: I233cb78b4695bbe00a4605ae64080a47629329b8
This commit is contained in:
committed by
Shentubot
parent
c348d07863
commit
a38f41b464
@@ -34,8 +34,34 @@ const (
|
||||
// Use virtual file system cache for everything, but send writes to the
|
||||
// fs agent immediately.
|
||||
cacheAllWritethrough
|
||||
|
||||
// Use virtual file system cache for everything, but reload dirents
|
||||
// from the remote filesystem on each lookup. Thus, if the remote
|
||||
// filesystem has changed, the returned dirent will have the updated
|
||||
// state.
|
||||
//
|
||||
// This policy should *only* be used with remote filesystems that
|
||||
// donate their host FDs to the sandbox and thus use the host page
|
||||
// cache, otherwise the dirent state will be inconsistent.
|
||||
cacheRemoteRevalidating
|
||||
)
|
||||
|
||||
// String returns the string name of the cache policy.
|
||||
func (cp cachePolicy) String() string {
|
||||
switch cp {
|
||||
case cacheNone:
|
||||
return "cacheNone"
|
||||
case cacheAll:
|
||||
return "cacheAll"
|
||||
case cacheAllWritethrough:
|
||||
return "cacheAllWritethrough"
|
||||
case cacheRemoteRevalidating:
|
||||
return "cacheRemoteRevalidating"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func parseCachePolicy(policy string) (cachePolicy, error) {
|
||||
switch policy {
|
||||
case "fscache":
|
||||
@@ -44,6 +70,8 @@ func parseCachePolicy(policy string) (cachePolicy, error) {
|
||||
return cacheNone, nil
|
||||
case "fscache_writethrough":
|
||||
return cacheAllWritethrough, nil
|
||||
case "remote_revalidating":
|
||||
return cacheRemoteRevalidating, nil
|
||||
}
|
||||
return cacheNone, fmt.Errorf("unsupported cache mode: %s", policy)
|
||||
}
|
||||
@@ -63,14 +91,16 @@ func (cp cachePolicy) cacheReaddir() bool {
|
||||
}
|
||||
|
||||
// usePageCache determines whether the page cache should be used for the given
|
||||
// inode.
|
||||
// inode. If the remote filesystem donates host FDs to the sentry, then the
|
||||
// host kernel's page cache will be used, otherwise we will use a
|
||||
// sentry-internal page cache.
|
||||
func (cp cachePolicy) usePageCache(inode *fs.Inode) bool {
|
||||
// Do cached IO for regular files only. Some "character devices" expect
|
||||
// no caching.
|
||||
if !fs.IsFile(inode.StableAttr) {
|
||||
return false
|
||||
}
|
||||
return cp == cacheAll || cp == cacheAllWritethrough
|
||||
return cp == cacheAll || cp == cacheAllWritethrough || cp == cacheRemoteRevalidating
|
||||
}
|
||||
|
||||
// writeThough indicates whether writes to the file should be synced to the
|
||||
@@ -79,10 +109,16 @@ func (cp cachePolicy) writeThrough(inode *fs.Inode) bool {
|
||||
return cp == cacheNone || cp == cacheAllWritethrough
|
||||
}
|
||||
|
||||
// revalidateDirent indicates that dirents should be revalidated after they are
|
||||
// looked up.
|
||||
// revalidateDirent indicates that a dirent should be revalidated after a
|
||||
// lookup, because the looked up version may be stale.
|
||||
func (cp cachePolicy) revalidateDirent() bool {
|
||||
return cp == cacheNone
|
||||
if cp == cacheAll || cp == cacheAllWritethrough {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: The cacheRemoteRevalidating policy should only
|
||||
// return true if the remote file's attributes have changed.
|
||||
return true
|
||||
}
|
||||
|
||||
// keepDirent indicates that dirents should be kept pinned in the dirent tree
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package gofer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||
@@ -72,6 +73,17 @@ func NewFile(ctx context.Context, dirent *fs.Dirent, name string, flags fs.FileF
|
||||
flags.Pread = true
|
||||
flags.Pwrite = true
|
||||
|
||||
if fs.IsFile(dirent.Inode.StableAttr) {
|
||||
// If cache policy is "remote revalidating", then we must
|
||||
// ensure that we have a host FD. Otherwise, the
|
||||
// sentry-internal page cache will be used, and we can end up
|
||||
// in an inconsistent state if the remote file changes.
|
||||
cp := dirent.Inode.InodeOperations.(*inodeOperations).session().cachePolicy
|
||||
if cp == cacheRemoteRevalidating && handles.Host == nil {
|
||||
panic(fmt.Sprintf("remote-revalidating cache policy requires gofer to donate host FD, but file %q did not have host FD", name))
|
||||
}
|
||||
}
|
||||
|
||||
f := &fileOperations{
|
||||
inodeOperations: i,
|
||||
handles: handles,
|
||||
@@ -202,7 +214,6 @@ func (f *fileOperations) Write(ctx context.Context, file *fs.File, src usermem.I
|
||||
err = f.inodeOperations.cachingInodeOps.WriteOut(ctx, file.Dirent.Inode)
|
||||
}
|
||||
return n, err
|
||||
|
||||
}
|
||||
return src.CopyInTo(ctx, f.handles.readWriterAt(ctx, offset))
|
||||
}
|
||||
|
||||
+339
-226
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user