Browse code

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 authored on 2016/08/23 03:43:10
Showing 2 changed files
... ...
@@ -31,12 +31,12 @@ type mountOptions struct {
31 31
 	Flag   uint32
32 32
 }
33 33
 
34
-func mountFrom(dir, device, target, mType, label string) error {
34
+func mountFrom(dir, device, target, mType string, flags uintptr, label string) error {
35 35
 	options := &mountOptions{
36 36
 		Device: device,
37 37
 		Target: target,
38 38
 		Type:   mType,
39
-		Flag:   0,
39
+		Flag:   uint32(flags),
40 40
 		Label:  label,
41 41
 	}
42 42
 
... ...
@@ -409,13 +409,36 @@ func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
409 409
 	}()
410 410
 
411 411
 	workDir := path.Join(dir, "work")
412
-	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
413
-	mountLabel = label.FormatMountLabel(opts, mountLabel)
414
-	if len(mountLabel) > syscall.Getpagesize() {
415
-		return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountLabel))
412
+	splitLowers := strings.Split(string(lowers), ":")
413
+	absLowers := make([]string, len(splitLowers))
414
+	for i, s := range splitLowers {
415
+		absLowers[i] = path.Join(d.home, s)
416
+	}
417
+	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work"))
418
+	mountData := label.FormatMountLabel(opts, mountLabel)
419
+	mount := syscall.Mount
420
+	mountTarget := mergedDir
421
+
422
+	pageSize := syscall.Getpagesize()
423
+
424
+	// Use relative paths and mountFrom when the mount data has exceeded
425
+	// the page size. The mount syscall fails if the mount data cannot
426
+	// fit within a page and relative links make the mount data much
427
+	// smaller at the expense of requiring a fork exec to chroot.
428
+	if len(mountData) > pageSize {
429
+		opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
430
+		mountData = label.FormatMountLabel(opts, mountLabel)
431
+		if len(mountData) > pageSize {
432
+			return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
433
+		}
434
+
435
+		mount = func(source string, target string, mType string, flags uintptr, label string) error {
436
+			return mountFrom(d.home, source, target, mType, flags, label)
437
+		}
438
+		mountTarget = path.Join(id, "merged")
416 439
 	}
417 440
 
418
-	if err := mountFrom(d.home, "overlay", path.Join(id, "merged"), "overlay", mountLabel); err != nil {
441
+	if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil {
419 442
 		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
420 443
 	}
421 444