CPULimiter: Refactor class to manage the CPU limitation.
This new class replaces the functionality embedded in UpdateAttempter
that limits the max CPU usage allowed by update_engine. This refactor
helps reusing this class outside of the brillo UpdateAttempter.
Bug: None
TEST=FEATURES=test emerge-link update_engine
Change-Id: Ib5487d314846b497a44bb78a3b94609571e0fe38
diff --git a/common/cpu_limiter.cc b/common/cpu_limiter.cc
new file mode 100644
index 0000000..67c50b6
--- /dev/null
+++ b/common/cpu_limiter.cc
@@ -0,0 +1,88 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/cpu_limiter.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/utils.h"
+
+namespace {
+
+// Cgroup container is created in update-engine's upstart script located at
+// /etc/init/update-engine.conf.
+const char kCGroupSharesPath[] = "/sys/fs/cgroup/cpu/update-engine/cpu.shares";
+
+} // namespace
+
+namespace chromeos_update_engine {
+
+CPULimiter::~CPULimiter() {
+ // Set everything back to normal on destruction.
+ CPULimiter::SetCpuShares(CpuShares::kNormal);
+}
+
+void CPULimiter::StartLimiter() {
+ if (manage_shares_id_ != brillo::MessageLoop::kTaskIdNull) {
+ LOG(ERROR) << "Cpu shares timeout source hasn't been destroyed.";
+ StopLimiter();
+ }
+ manage_shares_id_ = brillo::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CPULimiter::StopLimiterCallback, base::Unretained(this)),
+ base::TimeDelta::FromHours(2));
+ SetCpuShares(CpuShares::kLow);
+}
+
+void CPULimiter::StopLimiter() {
+ if (manage_shares_id_ != brillo::MessageLoop::kTaskIdNull) {
+ // If the shares were never set and there isn't a message loop instance,
+ // we avoid calling CancelTask(), which otherwise would have been a no-op.
+ brillo::MessageLoop::current()->CancelTask(manage_shares_id_);
+ manage_shares_id_ = brillo::MessageLoop::kTaskIdNull;
+ }
+ SetCpuShares(CpuShares::kNormal);
+}
+
+bool CPULimiter::SetCpuShares(CpuShares shares) {
+ // Short-circuit to avoid re-setting the shares.
+ if (shares_ == shares)
+ return true;
+
+ std::string string_shares = base::IntToString(static_cast<int>(shares));
+ LOG(INFO) << "Setting cgroup cpu shares to " << string_shares;
+ if (!utils::WriteFile(
+ kCGroupSharesPath, string_shares.c_str(), string_shares.size())) {
+ LOG(ERROR) << "Failed to change cgroup cpu shares to " << string_shares
+ << " using " << kCGroupSharesPath;
+ return false;
+ }
+ shares_ = shares;
+ LOG(INFO) << "CPU shares = " << shares_;
+ return true;
+}
+
+void CPULimiter::StopLimiterCallback() {
+ SetCpuShares(CpuShares::kNormal);
+ manage_shares_id_ = brillo::MessageLoop::kTaskIdNull;
+}
+
+} // namespace chromeos_update_engine
diff --git a/common/cpu_limiter.h b/common/cpu_limiter.h
new file mode 100644
index 0000000..c7add89
--- /dev/null
+++ b/common/cpu_limiter.h
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_CPU_LIMITER_H_
+#define UPDATE_ENGINE_COMMON_CPU_LIMITER_H_
+
+#include <brillo/message_loops/message_loop.h>
+
+namespace chromeos_update_engine {
+
+// Cgroups cpu shares constants. 1024 is the default shares a standard process
+// gets and 2 is the minimum value. We set High as a value that gives the
+// update-engine 2x the cpu share of a standard process.
+enum class CpuShares : int {
+ kHigh = 2048,
+ kNormal = 1024,
+ kLow = 2,
+};
+
+// Sets the current process shares to |shares|. Returns true on
+// success, false otherwise.
+bool SetCpuShares(CpuShares shares);
+
+class CPULimiter {
+ public:
+ CPULimiter() = default;
+ ~CPULimiter();
+
+ // Sets the cpu shares to low and sets up timeout events to stop the limiter.
+ void StartLimiter();
+
+ // Resets the cpu shares to normal and destroys any scheduled timeout sources.
+ void StopLimiter();
+
+ // Sets the cpu shares to |shares|. This method can be user at any time, but
+ // if the limiter is not running, the shares won't be reset to normal.
+ bool SetCpuShares(CpuShares shares);
+
+ private:
+ // The cpu shares timeout source callback sets the current cpu shares to
+ // normal.
+ void StopLimiterCallback();
+
+ // Current cpu shares.
+ CpuShares shares_ = CpuShares::kNormal;
+
+ // The cpu shares management timeout task id.
+ brillo::MessageLoop::TaskId manage_shares_id_{
+ brillo::MessageLoop::kTaskIdNull};
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_COMMON_CPU_LIMITER_H_
diff --git a/common/cpu_limiter_unittest.cc b/common/cpu_limiter_unittest.cc
new file mode 100644
index 0000000..d549b4c
--- /dev/null
+++ b/common/cpu_limiter_unittest.cc
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/cpu_limiter.h"
+
+#include <gtest/gtest.h>
+
+namespace chromeos_update_engine {
+
+class CPULimiterTest : public ::testing::Test {};
+
+namespace {
+// Compares cpu shares and returns an integer that is less
+// than, equal to or greater than 0 if |shares_lhs| is,
+// respectively, lower than, same as or higher than |shares_rhs|.
+int CompareCpuShares(CpuShares shares_lhs, CpuShares shares_rhs) {
+ return static_cast<int>(shares_lhs) - static_cast<int>(shares_rhs);
+}
+} // namespace
+
+// Tests the CPU shares enum is in the order we expect it.
+TEST(CPULimiterTest, CompareCpuSharesTest) {
+ EXPECT_LT(CompareCpuShares(CpuShares::kLow, CpuShares::kNormal), 0);
+ EXPECT_GT(CompareCpuShares(CpuShares::kNormal, CpuShares::kLow), 0);
+ EXPECT_EQ(CompareCpuShares(CpuShares::kNormal, CpuShares::kNormal), 0);
+ EXPECT_GT(CompareCpuShares(CpuShares::kHigh, CpuShares::kNormal), 0);
+}
+
+} // namespace chromeos_update_engine
diff --git a/common/utils.cc b/common/utils.cc
index d3b5baa..41a75f8 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -157,10 +157,6 @@
namespace utils {
-// Cgroup container is created in update-engine's upstart script located at
-// /etc/init/update-engine.conf.
-static const char kCGroupDir[] = "/sys/fs/cgroup/cpu/update-engine";
-
string ParseECVersion(string input_line) {
base::TrimWhitespaceASCII(input_line, base::TRIM_ALL, &input_line);
@@ -885,20 +881,6 @@
base::Bind(&TriggerCrashReporterUpload));
}
-bool SetCpuShares(CpuShares shares) {
- string string_shares = base::IntToString(static_cast<int>(shares));
- string cpu_shares_file = string(utils::kCGroupDir) + "/cpu.shares";
- LOG(INFO) << "Setting cgroup cpu shares to " << string_shares;
- if (utils::WriteFile(cpu_shares_file.c_str(), string_shares.c_str(),
- string_shares.size())) {
- return true;
- } else {
- LOG(ERROR) << "Failed to change cgroup cpu shares to "<< string_shares
- << " using " << cpu_shares_file;
- return false;
- }
-}
-
int FuzzInt(int value, unsigned int range) {
int min = value - range / 2;
int max = value + range - range / 2;
diff --git a/common/utils.h b/common/utils.h
index 0440dd8..ecb7fb9 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -277,19 +277,6 @@
}
}
-// Cgroups cpu shares constants. 1024 is the default shares a standard process
-// gets and 2 is the minimum value. We set High as a value that gives the
-// update-engine 2x the cpu share of a standard process.
-enum CpuShares {
- kCpuSharesHigh = 2048,
- kCpuSharesNormal = 1024,
- kCpuSharesLow = 2,
-};
-
-// Sets the current process shares to |shares|. Returns true on
-// success, false otherwise.
-bool SetCpuShares(CpuShares shares);
-
// Converts seconds into human readable notation including days, hours, minutes
// and seconds. For example, 185 will yield 3m5s, 4300 will yield 1h11m40s, and
// 360000 will yield 4d4h0m0s. Zero padding not applied. Seconds are always
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index 02f919e..fde89e8 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -180,28 +180,6 @@
utils::MakePartitionNameForMount("/dev/ubiblock1"));
}
-namespace {
-// Compares cpu shares and returns an integer that is less
-// than, equal to or greater than 0 if |shares_lhs| is,
-// respectively, lower than, same as or higher than |shares_rhs|.
-int CompareCpuShares(utils::CpuShares shares_lhs,
- utils::CpuShares shares_rhs) {
- return static_cast<int>(shares_lhs) - static_cast<int>(shares_rhs);
-}
-} // namespace
-
-// Tests the CPU shares enum is in the order we expect it.
-TEST(UtilsTest, CompareCpuSharesTest) {
- EXPECT_LT(CompareCpuShares(utils::kCpuSharesLow,
- utils::kCpuSharesNormal), 0);
- EXPECT_GT(CompareCpuShares(utils::kCpuSharesNormal,
- utils::kCpuSharesLow), 0);
- EXPECT_EQ(CompareCpuShares(utils::kCpuSharesNormal,
- utils::kCpuSharesNormal), 0);
- EXPECT_GT(CompareCpuShares(utils::kCpuSharesHigh,
- utils::kCpuSharesNormal), 0);
-}
-
TEST(UtilsTest, FuzzIntTest) {
static const unsigned int kRanges[] = { 0, 1, 2, 20 };
for (unsigned int range : kRanges) {