summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2021-04-19 16:22:32 -0700
committergVisor bot <gvisor-bot@google.com>2021-04-19 16:25:01 -0700
commit276ff149a4555b69c4c99fdcd4e1a22ccc8b9463 (patch)
tree744b3618a19f16b45b4fe6bfa584dc6a9b2e00ad /runsc
parent8ad6657a22b0eaaef7d1b4a31553e826a87e9190 (diff)
Add MultiGetAttr message to 9P
While using remote-validation, the vast majority of time spent during FS operations is re-walking the path to check for modifications and then closing the file given that in most cases it has not been modified externally. This change introduces a new 9P message called MultiGetAttr which bulks query attributes of several files in one shot. The returned attributes are then used to update cached dentries before they are walked. File attributes are updated for files that still exist. Dentries that have been deleted are removed from the cache. And negative cache entries are removed if a new file/directory was created externally. Similarly, synthetic dentries are replaced if a file/directory is created externally. The bulk update needs to be carefull not to follow symlinks, cross mount points, because the gofer doesn't know how to resolve symlinks and where mounts points are located. It also doesn't walk to the parent ("..") to avoid deadlocks. Here are the results: Workload VFS1 VFS2 Change bazel action 115s 70s 28.8s Stat/100 11,043us 7,623us 974us Updates #1638 PiperOrigin-RevId: 369325957
Diffstat (limited to 'runsc')
-rw-r--r--runsc/fsgofer/fsgofer.go57
-rw-r--r--runsc/fsgofer/fsgofer_test.go10
2 files changed, 54 insertions, 13 deletions
diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go
index e04ddda47..b81ede5ae 100644
--- a/runsc/fsgofer/fsgofer.go
+++ b/runsc/fsgofer/fsgofer.go
@@ -21,6 +21,7 @@
package fsgofer
import (
+ "errors"
"fmt"
"io"
"math"
@@ -58,9 +59,6 @@ var verityXattrs = map[string]struct{}{
// join is equivalent to path.Join() but skips path.Clean() which is expensive.
func join(parent, child string) string {
- if child == "." || child == ".." {
- panic(fmt.Sprintf("invalid child path %q", child))
- }
return parent + "/" + child
}
@@ -1226,3 +1224,56 @@ func (l *localFile) checkROMount() error {
}
return nil
}
+
+func (l *localFile) MultiGetAttr(names []string) ([]p9.FullStat, error) {
+ stats := make([]p9.FullStat, 0, len(names))
+
+ if len(names) > 0 && names[0] == "" {
+ qid, valid, attr, err := l.GetAttr(p9.AttrMask{})
+ if err != nil {
+ return nil, err
+ }
+ stats = append(stats, p9.FullStat{
+ QID: qid,
+ Valid: valid,
+ Attr: attr,
+ })
+ names = names[1:]
+ }
+
+ parent := l.file.FD()
+ for _, name := range names {
+ child, err := unix.Openat(parent, name, openFlags|unix.O_PATH, 0)
+ if parent != l.file.FD() {
+ // Parent is no longer needed.
+ _ = unix.Close(parent)
+ }
+ if err != nil {
+ if errors.Is(err, unix.ENOENT) {
+ // No pont in continuing any further.
+ break
+ }
+ return nil, err
+ }
+
+ var stat unix.Stat_t
+ if err := unix.Fstat(child, &stat); err != nil {
+ _ = unix.Close(child)
+ return nil, err
+ }
+ valid, attr := l.fillAttr(&stat)
+ stats = append(stats, p9.FullStat{
+ QID: l.attachPoint.makeQID(&stat),
+ Valid: valid,
+ Attr: attr,
+ })
+ if (stat.Mode & unix.S_IFMT) != unix.S_IFDIR {
+ // Doesn't need to continue if entry is not a dir. Including symlinks
+ // that cannot be followed.
+ _ = unix.Close(child)
+ break
+ }
+ parent = child
+ }
+ return stats, nil
+}
diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go
index d7e141476..77723827a 100644
--- a/runsc/fsgofer/fsgofer_test.go
+++ b/runsc/fsgofer/fsgofer_test.go
@@ -703,16 +703,6 @@ func TestWalkNotFound(t *testing.T) {
})
}
-func TestWalkPanic(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, allConfs, func(t *testing.T, s state) {
- for _, name := range []string{".", ".."} {
- assertPanic(t, func() {
- s.file.Walk([]string{name})
- })
- }
- })
-}
-
func TestWalkDup(t *testing.T) {
runAll(t, func(t *testing.T, s state) {
_, dup, err := s.file.Walk([]string{})