Prechádzať zdrojové kódy

Avoid fork on mount for overlay2 in common case

In the common case where the user is using /var/lib/docker and
an image with less than 60 layers, forking is not needed. Calculate
whether absolute paths can be used and avoid forking to mount in
those cases.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
Derek McGowan 9 rokov pred
rodič
commit
c13a985fa1

+ 2 - 2
daemon/graphdriver/overlay2/mount.go

@@ -31,12 +31,12 @@ type mountOptions struct {
 	Flag   uint32
 }
 
-func mountFrom(dir, device, target, mType, label string) error {
+func mountFrom(dir, device, target, mType string, flags uintptr, label string) error {
 	options := &mountOptions{
 		Device: device,
 		Target: target,
 		Type:   mType,
-		Flag:   0,
+		Flag:   uint32(flags),
 		Label:  label,
 	}
 

+ 28 - 5
daemon/graphdriver/overlay2/overlay.go

@@ -409,13 +409,36 @@ func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
 	}()
 
 	workDir := path.Join(dir, "work")
-	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
-	mountLabel = label.FormatMountLabel(opts, mountLabel)
-	if len(mountLabel) > syscall.Getpagesize() {
-		return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountLabel))
+	splitLowers := strings.Split(string(lowers), ":")
+	absLowers := make([]string, len(splitLowers))
+	for i, s := range splitLowers {
+		absLowers[i] = path.Join(d.home, s)
+	}
+	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work"))
+	mountData := label.FormatMountLabel(opts, mountLabel)
+	mount := syscall.Mount
+	mountTarget := mergedDir
+
+	pageSize := syscall.Getpagesize()
+
+	// Use relative paths and mountFrom when the mount data has exceeded
+	// the page size. The mount syscall fails if the mount data cannot
+	// fit within a page and relative links make the mount data much
+	// smaller at the expense of requiring a fork exec to chroot.
+	if len(mountData) > pageSize {
+		opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
+		mountData = label.FormatMountLabel(opts, mountLabel)
+		if len(mountData) > pageSize {
+			return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
+		}
+
+		mount = func(source string, target string, mType string, flags uintptr, label string) error {
+			return mountFrom(d.home, source, target, mType, flags, label)
+		}
+		mountTarget = path.Join(id, "merged")
 	}
 
-	if err := mountFrom(d.home, "overlay", path.Join(id, "merged"), "overlay", mountLabel); err != nil {
+	if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil {
 		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
 	}