Bug 1082290 - Add memory cgroup support to FxOS process control. r=gsvelto
authorDave Hylands <dhylands@mozilla.com>
Tue, 13 Jan 2015 12:12:32 -0800
changeset 223642 f9625445803ad26ece206427bafe4cfde2fa9e63
parent 223641 1ba6070d3f51a8e9a5dbd2a69326d557ee503cc9
child 223643 754b38b36e683e0d559c8fde6185564f4ae5ad28
push id28099
push userkwierso@gmail.com
push dateWed, 14 Jan 2015 01:38:51 +0000
treeherdermozilla-central@3100ceecc1bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgsvelto
bugs1082290
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1082290 - Add memory cgroup support to FxOS process control. r=gsvelto
b2g/app/b2g.js
hal/gonk/GonkHal.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -725,32 +725,40 @@ pref("hal.processPriorityManager.gonk.BA
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.cgroup", "apps/bg_non_interactive");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 667);
 pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderKB", 20480);
 pref("hal.processPriorityManager.gonk.BACKGROUND.cgroup", "apps/bg_non_interactive");
 
 // Control group definitions (i.e., CPU priority groups) for B2G processes.
+//
+// memory_swappiness -   0 - The kernel will swap only to avoid an out of memory condition
+// memory_swappiness -  60 - The default value.
+// memory_swappiness - 100 - The kernel will swap aggressively.
 
 // Foreground apps
 pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_shares", 1024);
 pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_notify_on_migrate", 1);
+pref("hal.processPriorityManager.gonk.cgroups.apps.memory_swappiness", 10);
 
 // Foreground apps with high priority, 16x more CPU than foreground ones
 pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_shares", 16384);
 pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_notify_on_migrate", 1);
+pref("hal.processPriorityManager.gonk.cgroups.apps/critical.memory_swappiness", 0);
 
 // Background perceivable apps, ~10x less CPU than foreground ones
 pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_shares", 103);
 pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_notify_on_migrate", 0);
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.memory_swappiness", 60);
 
 // Background apps, ~20x less CPU than foreground ones and ~2x less than perceivable ones
 pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_shares", 52);
 pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_notify_on_migrate", 0);
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.memory_swappiness", 100);
 
 // By default the compositor thread on gonk runs without real-time priority.  RT
 // priority can be enabled by setting this pref to a value between 1 and 99.
 // Note that audio processing currently runs at RT priority 2 or 3 at most.
 //
 // If RT priority is disabled, then the compositor nice value is used. We prefer
 // to use a nice value of -4, which matches Android's preferences. Setting a preference
 // of RT priority 1 would mean it is higher than audio, which is -16. The compositor
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -1318,67 +1318,116 @@ public:
    * @param aPid The PID of the process to be added.
    */
   void AddProcess(int aPid);
 
 private:
   ProcessPriority mPriority;
   int32_t mOomScoreAdj;
   int32_t mKillUnderKB;
-  int mCGroupProcsFd;
+  int mCpuCGroupProcsFd;
+  int mMemCGroupProcsFd;
   nsCString mGroup;
 
   /**
    * Return a string that identifies where we can find the value of aPref
    * that's specific to mPriority.  For example, we might return
    * "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
    */
   nsCString PriorityPrefName(const char* aPref)
   {
     return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
                            ProcessPriorityToString(mPriority), aPref);
   }
 
   /**
    * Get the full path of the cgroup.procs file associated with the group.
    */
-  nsCString CGroupProcsFilename()
+  nsCString CpuCGroupProcsFilename()
   {
     nsCString cgroupName = mGroup;
 
     /* If mGroup is empty, our cgroup.procs file is the root procs file,
      * located at /dev/cpuctl/cgroup.procs.  Otherwise our procs file is
      * /dev/cpuctl/NAME/cgroup.procs. */
 
     if (!mGroup.IsEmpty()) {
       cgroupName.AppendLiteral("/");
     }
 
     return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
            NS_LITERAL_CSTRING("cgroup.procs");
   }
 
-  int OpenCGroupProcs()
+  nsCString MemCGroupProcsFilename()
   {
-    return open(CGroupProcsFilename().get(), O_WRONLY);
+    nsCString cgroupName = mGroup;
+
+    /* If mGroup is empty, our cgroup.procs file is the root procs file,
+     * located at /sys/fs/cgroup/memory/cgroup.procs.  Otherwise our procs
+     * file is /sys/fs/cgroup/memory/NAME/cgroup.procs. */
+
+    if (!mGroup.IsEmpty()) {
+      cgroupName.AppendLiteral("/");
+    }
+
+    return NS_LITERAL_CSTRING("/sys/fs/cgroup/memory/") + cgroupName +
+           NS_LITERAL_CSTRING("cgroup.procs");
+  }
+
+  int OpenCpuCGroupProcs()
+  {
+    return open(CpuCGroupProcsFilename().get(), O_WRONLY);
+  }
+
+  int OpenMemCGroupProcs()
+  {
+    return open(MemCGroupProcsFilename().get(), O_WRONLY);
   }
 };
 
 /**
+ * Creates a directory and parents (essentially mkdir -p, but
+ * this only create the directories within the cgroup name).
+ */
+static bool MakeCGroupDir(const nsACString& aRootDir,
+                          const nsACString& aGroupName)
+{
+  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
+
+  // Create directories contained within aGroupName
+  nsCString cgroupIter = aGroupName + kSlash;
+
+  int32_t offset = 0;
+  while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
+    nsAutoCString path = aRootDir + Substring(cgroupIter, 0, offset);
+    int rv = mkdir(path.get(), 0744);
+
+    if (rv == -1 && errno != EEXIST) {
+      HAL_LOG("Could not create the %s control group.", path.get());
+      return false;
+    }
+
+    offset++;
+  }
+  return true;
+}
+
+/**
  * Try to create the cgroup for the given PriorityClass, if it doesn't already
  * exist.  This essentially implements mkdir -p; that is, we create parent
  * cgroups as necessary.  The group parameters are also set according to
  * the corresponding preferences.
  *
  * @param aGroup The name of the group.
  * @return true if we successfully created the cgroup, or if it already
  * exists.  Otherwise, return false.
  */
 static bool
-EnsureCGroupExists(const nsACString &aGroup)
+EnsureCpuCGroupExists(const nsACString& aGroup)
 {
   NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
   NS_NAMED_LITERAL_CSTRING(kSlash, "/");
 
   nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
 
   /* If cgroup is not empty, append the cgroup name and a dot to obtain the
    * group specific preferences. */
@@ -1388,30 +1437,18 @@ EnsureCGroupExists(const nsACString &aGr
 
   nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
   int cpuShares = Preferences::GetInt(cpuSharesPref.get());
 
   nsAutoCString cpuNotifyOnMigratePref(prefPrefix
     + NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
   int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
 
-  // Create mCGroup and its parent directories, as necessary.
-  nsCString cgroupIter = aGroup + kSlash;
-
-  int32_t offset = 0;
-  while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
-    nsAutoCString path = kDevCpuCtl + Substring(cgroupIter, 0, offset);
-    int rv = mkdir(path.get(), 0744);
-
-    if (rv == -1 && errno != EEXIST) {
-      HAL_LOG("Could not create the %s control group.", path.get());
-      return false;
-    }
-
-    offset++;
+  if (!MakeCGroupDir(kDevCpuCtl, aGroup)) {
+    return false;
   }
 
   nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
   nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
   if (cpuShares && !WriteToFile(cpuSharesPath.get(),
                                 nsPrintfCString("%d", cpuShares).get())) {
     HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
     return false;
@@ -1424,73 +1461,122 @@ EnsureCGroupExists(const nsACString &aGr
     HAL_LOG("Could not set the cpu migration notification flag for group %s",
             notifyOnMigratePath.get());
     return false;
   }
 
   return true;
 }
 
+static bool
+EnsureMemCGroupExists(const nsACString& aGroup)
+{
+  NS_NAMED_LITERAL_CSTRING(kMemCtl, "/sys/fs/cgroup/memory/");
+  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
+
+  nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
+
+  /* If cgroup is not empty, append the cgroup name and a dot to obtain the
+   * group specific preferences. */
+  if (!aGroup.IsEmpty()) {
+    prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
+  }
+
+  nsAutoCString memSwappinessPref(prefPrefix +
+                                  NS_LITERAL_CSTRING("memory_swappiness"));
+  int memSwappiness = Preferences::GetInt(memSwappinessPref.get());
+
+  if (!MakeCGroupDir(kMemCtl, aGroup)) {
+    return false;
+  }
+
+  nsAutoCString pathPrefix(kMemCtl + aGroup + kSlash);
+  nsAutoCString memSwappinessPath(pathPrefix +
+                                  NS_LITERAL_CSTRING("memory.swappiness"));
+  if (!WriteToFile(memSwappinessPath.get(),
+                   nsPrintfCString("%d", memSwappiness).get())) {
+    HAL_LOG("Could not set the memory.swappiness for group %s",
+            memSwappinessPath.get());
+    return false;
+  }
+  return true;
+}
+
 PriorityClass::PriorityClass(ProcessPriority aPriority)
   : mPriority(aPriority)
   , mOomScoreAdj(0)
   , mKillUnderKB(0)
-  , mCGroupProcsFd(-1)
+  , mCpuCGroupProcsFd(-1)
+  , mMemCGroupProcsFd(-1)
 {
   DebugOnly<nsresult> rv;
 
   rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
                            &mOomScoreAdj);
   MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
 
   rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
                            &mKillUnderKB);
 
   rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
   MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
 
-  if (EnsureCGroupExists(mGroup)) {
-    mCGroupProcsFd = OpenCGroupProcs();
+  if (EnsureCpuCGroupExists(mGroup)) {
+    mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+  }
+  if (EnsureMemCGroupExists(mGroup)) {
+    mMemCGroupProcsFd = OpenMemCGroupProcs();
   }
 }
 
 PriorityClass::~PriorityClass()
 {
-  close(mCGroupProcsFd);
+  MOZ_TEMP_FAILURE_RETRY(close(mCpuCGroupProcsFd));
+  MOZ_TEMP_FAILURE_RETRY(close(mMemCGroupProcsFd));
 }
 
 PriorityClass::PriorityClass(const PriorityClass& aOther)
   : mPriority(aOther.mPriority)
   , mOomScoreAdj(aOther.mOomScoreAdj)
   , mKillUnderKB(aOther.mKillUnderKB)
   , mGroup(aOther.mGroup)
 {
-  mCGroupProcsFd = OpenCGroupProcs();
+  mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+  mMemCGroupProcsFd = OpenMemCGroupProcs();
 }
 
 PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
 {
   mPriority = aOther.mPriority;
   mOomScoreAdj = aOther.mOomScoreAdj;
   mKillUnderKB = aOther.mKillUnderKB;
   mGroup = aOther.mGroup;
-  mCGroupProcsFd = OpenCGroupProcs();
+  mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+  mMemCGroupProcsFd = OpenMemCGroupProcs();
   return *this;
 }
 
 void PriorityClass::AddProcess(int aPid)
 {
-  if (mCGroupProcsFd < 0) {
-    return;
+  if (mCpuCGroupProcsFd >= 0) {
+    nsPrintfCString str("%d", aPid);
+
+    if (write(mCpuCGroupProcsFd, str.get(), str.Length()) < 0) {
+      HAL_ERR("Couldn't add PID %d to the %s cpu control group",
+              aPid, mGroup.get());
+    }
   }
 
-  nsPrintfCString str("%d", aPid);
+  if (mMemCGroupProcsFd >= 0) {
+    nsPrintfCString str("%d", aPid);
 
-  if (write(mCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
-    HAL_ERR("Couldn't add PID %d to the %s control group", aPid, mGroup.get());
+    if (write(mMemCGroupProcsFd, str.get(), str.Length()) < 0) {
+      HAL_ERR("Couldn't add PID %d to the %s memory control group",
+              aPid, mGroup.get());
+    }
   }
 }
 
 /**
  * Get the PriorityClass associated with the given ProcessPriority.
  *
  * If you pass an invalid ProcessPriority value, we return null.
  *