Skip to content

Commit

Permalink
Expose config to cache ReadDir() response (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
raj-prince authored May 22, 2024
1 parent 39f95ce commit a2f23ee
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 0 deletions.
8 changes: 8 additions & 0 deletions conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,14 @@ func (c *Connection) kernelResponseForOp(
out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
out.Fh = uint64(o.Handle)

if o.CacheDir {
out.OpenFlags |= uint32(fusekernel.OpenCacheDir)
}

if o.KeepCache {
out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
}

case *fuseops.ReadDirOp:
// convertInMessage already set up the destination buffer to be at the end
// of the out message. We need only shrink to the right size based on how
Expand Down
8 changes: 8 additions & 0 deletions fuseops/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,14 @@ type OpenDirOp struct {
// a later call to ReleaseDirHandle.
Handle HandleID
OpContext OpContext

// CacheDir conveys to the kernel to cache the response of next
// ReadDirOp as page cache. Once cached, listing on that directory will be
// served from the kernel until invalidated.
CacheDir bool

// KeepCache instructs the kernel to not invalidate the data cache on open calls.
KeepCache bool
}

// Read entries from a directory previously opened with OpenDir.
Expand Down
2 changes: 2 additions & 0 deletions internal/fusekernel/fuse_kernel.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ const (
OpenDirectIO OpenResponseFlags = 1 << 0 // bypass page cache for this open file
OpenKeepCache OpenResponseFlags = 1 << 1 // don't invalidate the data cache on open
OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on OS X)
OpenCacheDir OpenResponseFlags = 1 << 3 // allow caching this directory

OpenPurgeAttr OpenResponseFlags = 1 << 30 // OS X
OpenPurgeUBC OpenResponseFlags = 1 << 31 // OS X
Expand All @@ -240,6 +241,7 @@ var openResponseFlagNames = []flagName{
{uint32(OpenDirectIO), "OpenDirectIO"},
{uint32(OpenKeepCache), "OpenKeepCache"},
{uint32(OpenNonSeekable), "OpenNonSeekable"},
{uint32(OpenCacheDir), "OpenCacheDir"},
{uint32(OpenPurgeAttr), "OpenPurgeAttr"},
{uint32(OpenPurgeUBC), "OpenPurgeUBC"},
}
Expand Down
89 changes: 89 additions & 0 deletions samples/cachingfs/caching_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ const (
// Each file responds to reads with random contents. SetKeepCache can be used
// to control whether the response to OpenFileOp tells the kernel to keep the
// file's data in the page cache or not.
//
// Each directory responds to readdir with random entries (different names).
// SetCacheDir and SetKeepDirCache can be used to control whether the response
// to OpenDirOp tells the kernel to cache the next response of ReadDirOp
// in cache, or invalidate the existing cached entry in page cache.
type CachingFS interface {
fuseutil.FileSystem

Expand All @@ -66,6 +71,14 @@ type CachingFS interface {
// Instruct the file system whether or not to reply to OpenFileOp with
// FOPEN_KEEP_CACHE set.
SetKeepCache(keep bool)

// Instruct the file system whether or not to reply to OpenDirOp with
// FOPEN_KEEP_CACHE set.
SetKeepDirCache(keep bool)

// Instruct the file system whether or not to reply to OpenDirOp with
// FOPEN_CACHE_DIR set.
SetCacheDir(cacheDir bool)
}

// Create a file system that issues cacheable responses according to the
Expand Down Expand Up @@ -126,6 +139,10 @@ type cachingFS struct {
// GUARDED_BY(mu)
keepPageCache bool

// GUARDED_BY(mu)
keepDirCache bool
cacheDir bool

// The current ID of the lowest numbered non-root inode.
//
// INVARIANT: baseID > fuseops.RootInodeID
Expand Down Expand Up @@ -202,6 +219,17 @@ func (fs *cachingFS) barAttrs() fuseops.InodeAttributes {
}
}

func randomString(len int) string {
bytes := make([]byte, len)
nn, err := rand.Read(bytes)

if err != nil || len != nn {
return ""
}

return string(bytes)
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -254,6 +282,22 @@ func (fs *cachingFS) SetKeepCache(keep bool) {
fs.keepPageCache = keep
}

// LOCKS_EXCLUDED(fs.mu)
func (fs *cachingFS) SetKeepDirCache(keep bool) {
fs.mu.Lock()
defer fs.mu.Unlock()

fs.keepDirCache = keep
}

// LOCKS_EXCLUDED(fs.mu)
func (fs *cachingFS) SetCacheDir(cacheDir bool) {
fs.mu.Lock()
defer fs.mu.Unlock()

fs.cacheDir = cacheDir
}

////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -349,6 +393,51 @@ func (fs *cachingFS) GetInodeAttributes(
func (fs *cachingFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) error {
fs.mu.Lock()
defer fs.mu.Unlock()

op.CacheDir = fs.cacheDir
op.KeepCache = fs.keepDirCache

return nil
}

func (fs *cachingFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) error {

entries := []fuseutil.Dirent{
{
Offset: 1,
Inode: 101,
Name: "rdir" + randomString(4),
Type: fuseutil.DT_Directory,
},
{
Offset: 2,
Inode: 102,
Name: "rfoo" + randomString(4),
Type: fuseutil.DT_File,
},
}

// Grab the range of interest.
if op.Offset > fuseops.DirOffset(len(entries)) {
return fuse.EIO
}

entries = entries[op.Offset:]

// Resume at the specified offset into the array.
for _, e := range entries {
n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
if n == 0 {
break
}

op.BytesRead += n
}

return nil
}

Expand Down
Loading

0 comments on commit a2f23ee

Please sign in to comment.