libstore: use vfork for linux sandbox launch wrappers
this halves sandbox launch overhead, reducing the build time for 3000 trivial runCommand derivations on our machine from 80 seconds to 44s. as a nice side effect we also get better error message propagation in some cases, most notably setgroups failing when run in lix sandboxes. Change-Id: Ia7c50a844915d9b8a20475b90b2d0179fd2fff34
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
---
|
||||
synopsis: "Linux sandbox launch overhead greatly reduced"
|
||||
cls: [5030]
|
||||
category: "Improvements"
|
||||
credits: [horrors]
|
||||
---
|
||||
|
||||
Sandboxed builds are now much cheaper to launch on Linux with 50% lower management
|
||||
overhead. This will mostly be noticeable when building derivation trees containing
|
||||
many small derivations like nixpkgs' `writeFile` or `runCommand` with scripts that
|
||||
very quickly. In synthetic tests we have seen build times of 3000 small runCommand
|
||||
drop from 80 seconds to 44 seconds, which is the most optimistic case in practice.
|
||||
@@ -1471,15 +1471,10 @@ Pid LinuxLocalDerivationGoal::startChild(
|
||||
return {std::move(userns), std::move(netns)};
|
||||
}();
|
||||
|
||||
Pipe sendPid;
|
||||
sendPid.create();
|
||||
|
||||
Pid helper = startProcess([&]() {
|
||||
Pid pid = inVFork(/* flags*/ 0, [&]() {
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
||||
throw SysError("setting death signal");
|
||||
|
||||
sendPid.readSide.close();
|
||||
|
||||
if (dup2(logPTY.get(), STDERR_FILENO) == -1) {
|
||||
throw SysError("failed to redirect build output to log file");
|
||||
}
|
||||
@@ -1504,25 +1499,17 @@ Pid LinuxLocalDerivationGoal::startChild(
|
||||
options.cloneFlags =
|
||||
CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
||||
|
||||
pid_t child = startProcess([&]() {
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
||||
throw SysError("setting death signal");
|
||||
runChild(builder, envStrs, args);
|
||||
}, options).release();
|
||||
|
||||
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||
_exit(0);
|
||||
return startProcess(
|
||||
[&]() {
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
|
||||
throw SysError("setting death signal");
|
||||
}
|
||||
runChild(builder, envStrs, args);
|
||||
},
|
||||
options
|
||||
);
|
||||
});
|
||||
|
||||
sendPid.writeSide.close();
|
||||
|
||||
if (helper.wait() != 0)
|
||||
throw Error("unable to start build process");
|
||||
|
||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||
assert(ss.size() == 1);
|
||||
Pid pid = Pid{string2Int<pid_t>(ss[0]).value()};
|
||||
|
||||
/* Migrate the child inside the available control group. */
|
||||
if (context.cgroup) {
|
||||
context.cgroup->adoptProcess(pid.get());
|
||||
|
||||
@@ -21,13 +21,13 @@ unshare --mount --map-root-user bash <<EOF
|
||||
# Fails with default setting
|
||||
# TODO better error
|
||||
setLocalStore store1
|
||||
expectStderr 1 "\${cmd[@]}" | grepQuiet "unable to start build process"
|
||||
expectStderr 1 "\${cmd[@]}" | grepQuiet "setgroups failed"
|
||||
|
||||
# Fails with `require-drop-supplementary-groups`
|
||||
# TODO better error
|
||||
setLocalStore store2
|
||||
NIX_CONFIG='require-drop-supplementary-groups = true' \
|
||||
expectStderr 1 "\${cmd[@]}" | grepQuiet "unable to start build process"
|
||||
expectStderr 1 "\${cmd[@]}" | grepQuiet "setgroups failed"
|
||||
|
||||
# Works without `require-drop-supplementary-groups`
|
||||
setLocalStore store3
|
||||
|
||||
Reference in New Issue
Block a user