From 34f459423ae32dd07993eebd7c81dc6390403b5a Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 11 Oct 2022 23:22:49 +0000 Subject: [PATCH] Replace overlay2 mount reexec with in-proc impl Building off insights from the great work Cory Snider has been doing, this replaces a reexec with a much lower overhead implementation which performs the `Chddir` in a new goroutine that is locked to a specific thread with CLONE_FS unshared. The thread is thrown away afterwards and the Chdir does effectively the same thing as what the reexec was being used for. Signed-off-by: Brian Goff --- daemon/graphdriver/overlay2/mount.go | 98 +++++----------------------- 1 file changed, 16 insertions(+), 82 deletions(-) diff --git a/daemon/graphdriver/overlay2/mount.go b/daemon/graphdriver/overlay2/mount.go index eff0f781bf..8d6c99195e 100644 --- a/daemon/graphdriver/overlay2/mount.go +++ b/daemon/graphdriver/overlay2/mount.go @@ -4,94 +4,28 @@ package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "os" "runtime" - "github.com/docker/docker/pkg/reexec" "golang.org/x/sys/unix" ) -func init() { - reexec.Register("docker-mountfrom", mountFromMain) -} - -func fatal(err error) { - fmt.Fprint(os.Stderr, err) - os.Exit(1) -} - -type mountOptions struct { - Device string - Target string - Type string - Label string - Flag uint32 -} - func mountFrom(dir, device, target, mType string, flags uintptr, label string) error { - options := &mountOptions{ - Device: device, - Target: target, - Type: mType, - Flag: uint32(flags), - Label: label, - } + chErr := make(chan error, 1) - cmd := reexec.Command("docker-mountfrom", dir) - w, err := cmd.StdinPipe() - if err != nil { - return fmt.Errorf("mountfrom error on pipe creation: %v", err) - } + go func() { + runtime.LockOSThread() + // Do not unlock this thread as the thread state cannot be restored + // We do not want go to re-use this thread for anything else. - output := bytes.NewBuffer(nil) - cmd.Stdout = output - cmd.Stderr = output - - // reexec.Command() sets cmd.SysProcAttr.Pdeathsig on Linux, which - // causes the started process to be signaled when the creating OS thread - // dies. Ensure that the reexec is not prematurely signaled. See - // https://go.dev/issue/27505 for more information. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - if err := cmd.Start(); err != nil { - w.Close() - return fmt.Errorf("mountfrom error on re-exec cmd: %v", err) - } - // write the options to the pipe for the untar exec to read - if err := json.NewEncoder(w).Encode(options); err != nil { - w.Close() - return fmt.Errorf("mountfrom json encode to pipe failed: %v", err) - } - w.Close() - - if err := cmd.Wait(); err != nil { - return fmt.Errorf("mountfrom re-exec error: %v: output: %v", err, output) - } - return nil -} - -// mountfromMain is the entry-point for docker-mountfrom on re-exec. -func mountFromMain() { - runtime.LockOSThread() - flag.Parse() - - var options *mountOptions - - if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil { - fatal(err) - } - - if err := os.Chdir(flag.Arg(0)); err != nil { - fatal(err) - } - - if err := unix.Mount(options.Device, options.Target, options.Type, uintptr(options.Flag), options.Label); err != nil { - fatal(err) - } - - os.Exit(0) + if err := unix.Unshare(unix.CLONE_FS); err != nil { + chErr <- err + return + } + if err := unix.Chdir(dir); err != nil { + chErr <- err + return + } + chErr <- unix.Mount(device, target, mType, flags, label) + }() + return <-chErr }