aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2020-05-16 21:09:48 +0200
committerEelco Dolstra <edolstra@gmail.com>2020-07-06 13:50:33 +0200
commit7bdcf43b401eba6aee29a359c5bce1f9cc01ce52 (patch)
tree2c45186654b9e1f7ef2ae3e2b246407aafbb95e0
parentca2f64bcdaef5915f5147eac935ecb770511e438 (diff)
Destroy the cgroup prior to building
-rw-r--r--src/libstore/build.cc8
-rw-r--r--src/libstore/cgroup.cc49
-rw-r--r--src/libstore/cgroup.hh13
3 files changed, 66 insertions, 4 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 816d695a5..1f1468d97 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -16,6 +16,7 @@
#include "machines.hh"
#include "daemon.hh"
#include "worker-protocol.hh"
+#include "cgroup.hh"
#include <algorithm>
#include <iostream>
@@ -2400,14 +2401,13 @@ void DerivationGoal::startBuilder()
auto hostCgroup = canonPath("/sys/fs/cgroup/" + name + "/" + cgroup);
if (!pathExists(hostCgroup))
- throw Error("expected unified cgroup directory '%s'", hostCgroup);
+ throw Error("expected cgroup directory '%s'", hostCgroup);
auto childCgroup = fmt("%s/nix-%d", hostCgroup, buildUser->getUID());
- // FIXME: if the cgroup already exists, kill all processes
- // in it and destroy it.
+ destroyCgroup(childCgroup);
- if (mkdir(childCgroup.c_str(), 0755) == -1 && errno != EEXIST)
+ if (mkdir(childCgroup.c_str(), 0755) == -1)
throw SysError("creating cgroup '%s'", childCgroup);
chownToBuilder(childCgroup);
diff --git a/src/libstore/cgroup.cc b/src/libstore/cgroup.cc
new file mode 100644
index 000000000..8cd682e68
--- /dev/null
+++ b/src/libstore/cgroup.cc
@@ -0,0 +1,49 @@
+#if __linux__
+
+#include "cgroup.hh"
+#include "util.hh"
+
+#include <chrono>
+
+#include <dirent.h>
+
+namespace nix {
+
+void destroyCgroup(const Path & cgroup)
+{
+ for (auto & entry : readDirectory(cgroup)) {
+ if (entry.type != DT_DIR) continue;
+ destroyCgroup(cgroup + "/" + entry.name);
+ }
+
+ int round = 1;
+
+ while (true) {
+ auto pids = tokenizeString<std::vector<std::string>>(readFile(cgroup + "/cgroup.procs"));
+
+ if (pids.empty()) break;
+
+ if (round > 20)
+ throw Error("cannot kill cgroup '%s'", cgroup);
+
+ for (auto & pid_s : pids) {
+ pid_t pid;
+ if (!string2Int(pid_s, pid)) throw Error("invalid pid '%s'", pid);
+ // FIXME: pid wraparound
+ if (kill(pid, SIGKILL) == -1 && errno != ESRCH)
+ throw SysError("killing member %d of cgroup '%s'", pid, cgroup);
+ }
+
+ auto sleep = std::chrono::milliseconds((int) std::pow(2.0, std::min(round, 10)));
+ printError("waiting for %d ms for cgroup '%s' to become empty", sleep.count(), cgroup);
+ std::this_thread::sleep_for(sleep);
+ round++;
+ }
+
+ if (rmdir(cgroup.c_str()) == -1)
+ throw SysError("deleting cgroup '%s'", cgroup);
+}
+
+}
+
+#endif
diff --git a/src/libstore/cgroup.hh b/src/libstore/cgroup.hh
new file mode 100644
index 000000000..c7b09398e
--- /dev/null
+++ b/src/libstore/cgroup.hh
@@ -0,0 +1,13 @@
+#pragma once
+
+#if __linux__
+
+#include "types.hh"
+
+namespace nix {
+
+void destroyCgroup(const Path & cgroup);
+
+}
+
+#endif