Bug 1081871 - Part 2 - Use cgroups instead of nice values to implement application priorities. r=dhylands
authorGabriele Svelto <gsvelto@mozilla.com>
Thu, 08 Jan 2015 15:39:43 +0100
changeset 222902 59452425e446c374a486169652abfff985df6759
parent 222901 a13166f0d914a85a083e5e22fc057be76538f0d8
child 222903 2e983faaa3a5b7751b856e9fba4af91a4659534c
push id10731
push usercbook@mozilla.com
push dateFri, 09 Jan 2015 14:51:37 +0000
treeherderfx-team@e6756043d930 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands
bugs1081871
milestone37.0a1
Bug 1081871 - Part 2 - Use cgroups instead of nice values to implement application priorities. r=dhylands
b2g/app/b2g.js
hal/gonk/GonkHal.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -690,60 +690,67 @@ pref("dom.ipc.processPriorityManager.tem
 // Number of different background levels for background processes.  We use
 // these different levels to force the low-memory killer to kill processes in
 // a LRU order.
 pref("dom.ipc.processPriorityManager.backgroundLRUPoolLevels", 5);
 
 // Kernel parameters for process priorities.  These affect how processes are
 // killed on low-memory and their relative CPU priorities.
 //
-// Note: The maximum nice value on Linux is 19, but the max value you should
-// use here is 18.  NSPR adds 1 to some threads' nice values, to mark
-// low-priority threads.  If the process priority manager were to renice a
-// process (and all its threads) to 19, all threads would have the same
-// niceness.  Then when we reniced the process to (say) 10, all threads would
-// /still/ have the same niceness; we'd effectively have erased NSPR's thread
-// priorities.
-
 // The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
 // okay, kernel will still kill processes with larger OomScoreAdjust first even
 // its OomScoreAdjust don't have a corresponding KillUnderKB.
 
 pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
 pref("hal.processPriorityManager.gonk.MASTER.KillUnderKB", 4096);
-pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
+pref("hal.processPriorityManager.gonk.MASTER.cgroup", "");
 
 pref("hal.processPriorityManager.gonk.PREALLOC.OomScoreAdjust", 67);
-pref("hal.processPriorityManager.gonk.PREALLOC.Nice", 18);
+pref("hal.processPriorityManager.gonk.PREALLOC.cgroup", "apps/bg_non_interactive");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderKB", 5120);
-pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.Nice", 0);
+pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.cgroup", "apps/critical");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
 pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderKB", 6144);
-pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
+pref("hal.processPriorityManager.gonk.FOREGROUND.cgroup", "apps");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
-pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.Nice", 1);
+pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.cgroup", "apps");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
 pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 7168);
-pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
+pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.cgroup", "apps/bg_perceivable");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 534);
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
-pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.Nice", 18);
+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.Nice", 18);
+pref("hal.processPriorityManager.gonk.BACKGROUND.cgroup", "apps/bg_non_interactive");
+
+// Control group definitions (i.e., CPU priority groups) for B2G processes.
+
+// Foreground apps
+pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_shares", 1024);
+pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_notify_on_migrate", 1);
 
-// Processes get this niceness when they have low CPU priority.
-pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
+// 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);
+
+// 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);
+
+// 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);
 
 // 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
@@ -19,16 +19,17 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/android_alarm.h>
 #include <math.h>
 #include <regex.h>
 #include <sched.h>
 #include <stdio.h>
 #include <sys/klog.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/resource.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "mozilla/DebugOnly.h"
 
 #include "android/log.h"
@@ -42,16 +43,17 @@
 #include "utils/threads.h"
 
 #include "base/message_loop.h"
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "HalLog.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/battery/Constants.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Preferences.h"
@@ -1259,16 +1261,271 @@ OomVictimLogger::Observe(
         break;
       }
     }
   }
 
   return NS_OK;
 }
 
+/**
+ * Wraps a particular ProcessPriority, giving us easy access to the prefs that
+ * are relevant to it.
+ *
+ * Creating a PriorityClass also ensures that the control group is created.
+ */
+class PriorityClass
+{
+public:
+  /**
+   * Create a PriorityClass for the given ProcessPriority.  This implicitly
+   * reads the relevant prefs and opens the cgroup.procs file of the relevant
+   * control group caching its file descriptor for later use.
+   */
+  PriorityClass(ProcessPriority aPriority);
+
+  /**
+   * Closes the file descriptor for the cgroup.procs file of the associated
+   * control group.
+   */
+  ~PriorityClass();
+
+  PriorityClass(const PriorityClass& aOther);
+  PriorityClass& operator=(const PriorityClass& aOther);
+
+  ProcessPriority Priority()
+  {
+    return mPriority;
+  }
+
+  int32_t OomScoreAdj()
+  {
+    return clamped<int32_t>(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
+  }
+
+  int32_t KillUnderKB()
+  {
+    return mKillUnderKB;
+  }
+
+  nsCString CGroup()
+  {
+    return mGroup;
+  }
+
+  /**
+   * Adds a process to this priority class, this moves the process' PID into
+   * the associated control group.
+   *
+   * @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;
+  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 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()
+  {
+    return open(CGroupProcsFilename().get(), O_WRONLY);
+  }
+};
+
+/**
+ * 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)
+{
+  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. */
+  if (!aGroup.IsEmpty()) {
+    prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
+  }
+
+  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++;
+  }
+
+  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;
+  }
+
+  nsAutoCString notifyOnMigratePath(pathPrefix
+    + NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
+  if (!WriteToFile(notifyOnMigratePath.get(),
+                   nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
+    HAL_LOG("Could not set the cpu migration notification flag for group %s",
+            notifyOnMigratePath.get());
+    return false;
+  }
+
+  return true;
+}
+
+PriorityClass::PriorityClass(ProcessPriority aPriority)
+  : mPriority(aPriority)
+  , mOomScoreAdj(0)
+  , mKillUnderKB(0)
+  , mCGroupProcsFd(-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();
+  }
+}
+
+PriorityClass::~PriorityClass()
+{
+  close(mCGroupProcsFd);
+}
+
+PriorityClass::PriorityClass(const PriorityClass& aOther)
+  : mPriority(aOther.mPriority)
+  , mOomScoreAdj(aOther.mOomScoreAdj)
+  , mKillUnderKB(aOther.mKillUnderKB)
+  , mGroup(aOther.mGroup)
+{
+  mCGroupProcsFd = OpenCGroupProcs();
+}
+
+PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
+{
+  mPriority = aOther.mPriority;
+  mOomScoreAdj = aOther.mOomScoreAdj;
+  mKillUnderKB = aOther.mKillUnderKB;
+  mGroup = aOther.mGroup;
+  mCGroupProcsFd = OpenCGroupProcs();
+  return *this;
+}
+
+void PriorityClass::AddProcess(int aPid)
+{
+  if (mCGroupProcsFd < 0) {
+    return;
+  }
+
+  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());
+  }
+}
+
+/**
+ * Get the PriorityClass associated with the given ProcessPriority.
+ *
+ * If you pass an invalid ProcessPriority value, we return null.
+ *
+ * The pointers returned here are owned by GetPriorityClass (don't free them
+ * yourself).  They are guaranteed to stick around until shutdown.
+ */
+PriorityClass*
+GetPriorityClass(ProcessPriority aPriority)
+{
+  static StaticAutoPtr<nsTArray<PriorityClass>> priorityClasses;
+
+  // Initialize priorityClasses if this is the first time we're running this
+  // method.
+  if (!priorityClasses) {
+    priorityClasses = new nsTArray<PriorityClass>();
+    ClearOnShutdown(&priorityClasses);
+
+    for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
+      priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
+    }
+  }
+
+  if (aPriority < 0 ||
+      static_cast<uint32_t>(aPriority) >= priorityClasses->Length()) {
+    return nullptr;
+  }
+
+  return &(*priorityClasses)[aPriority];
+}
+
 static void
 EnsureKernelLowMemKillerParamsSet()
 {
   static bool kernelLowMemKillerParamsSet;
   if (kernelLowMemKillerParamsSet) {
     return;
   }
   kernelLowMemKillerParamsSet = true;
@@ -1299,31 +1556,22 @@ EnsureKernelLowMemKillerParamsSet()
   int32_t countOfLowmemorykillerParametersSets = 0;
 
   long page_size = sysconf(_SC_PAGESIZE);
 
   for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
     // The system doesn't function correctly if we're missing these prefs, so
     // crash loudly.
 
-    ProcessPriority priority = static_cast<ProcessPriority>(i);
+    PriorityClass* pc = GetPriorityClass(static_cast<ProcessPriority>(i));
 
-    int32_t oomScoreAdj;
-    if (!NS_SUCCEEDED(Preferences::GetInt(
-          nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
-                          ProcessPriorityToString(priority)).get(),
-          &oomScoreAdj))) {
-      MOZ_CRASH();
-    }
+    int32_t oomScoreAdj = pc->OomScoreAdj();
+    int32_t killUnderKB = pc->KillUnderKB();
 
-    int32_t killUnderKB;
-    if (!NS_SUCCEEDED(Preferences::GetInt(
-          nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB",
-                          ProcessPriorityToString(priority)).get(),
-          &killUnderKB))) {
+    if (killUnderKB == 0) {
       // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
       // which has only OomScoreAdjust but lacks KillUnderMB value, will not
       // create new LMK parameters.
       continue;
     }
 
     // The LMK in kernel silently malfunctions if we assign the parameters
     // in non-increasing order, so we add this assertion here. See bug 887192.
@@ -1344,17 +1592,18 @@ EnsureKernelLowMemKillerParamsSet()
     countOfLowmemorykillerParametersSets++;
   }
 
   // Strip off trailing commas.
   adjParams.Cut(adjParams.Length() - 1, 1);
   minfreeParams.Cut(minfreeParams.Length() - 1, 1);
   if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
     WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
-    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get());
+    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree",
+                minfreeParams.get());
   }
 
   // Set the low-memory-notification threshold.
   int32_t lowMemNotifyThresholdKB;
   if (NS_SUCCEEDED(Preferences::GetInt(
         "hal.processPriorityManager.gonk.notifyLowMemUnderKB",
         &lowMemNotifyThresholdKB))) {
 
@@ -1366,158 +1615,16 @@ EnsureKernelLowMemKillerParamsSet()
   // Ensure OOM events appear in logcat
   nsRefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->AddObserver(oomLogger, "ipc:content-shutdown", false);
   }
 }
 
-static void
-SetNiceForPid(int aPid, int aNice)
-{
-  errno = 0;
-  int origProcPriority = getpriority(PRIO_PROCESS, aPid);
-  if (errno) {
-    HAL_LOG("Unable to get nice for pid=%d; error %d.  SetNiceForPid bailing.",
-            aPid, errno);
-    return;
-  }
-
-  int rv = setpriority(PRIO_PROCESS, aPid, aNice);
-  if (rv) {
-    HAL_LOG("Unable to set nice for pid=%d; error %d.  SetNiceForPid bailing.",
-            aPid, errno);
-    return;
-  }
-
-  // On Linux, setpriority(aPid) modifies the priority only of the main
-  // thread of that process.  We have to modify the priorities of all of the
-  // process's threads as well, so iterate over all the threads and increase
-  // each of their priorites by aNice - origProcPriority (and also ensure that
-  // none of the tasks has a lower priority than the main thread).
-  //
-  // This is horribly racy.
-
-  DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
-  if (!tasksDir) {
-    HAL_LOG("Unable to open /proc/%d/task.  SetNiceForPid bailing.", aPid);
-    return;
-  }
-
-  // Be careful not to leak tasksDir; after this point, we must call closedir().
-
-  while (struct dirent* de = readdir(tasksDir)) {
-    char* endptr = nullptr;
-    long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
-    if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
-      // if dp->d_name was not an integer, was negative (?!) or too large, or
-      // was the same as aPid, we're not interested.
-      //
-      // (The |tidlong == aPid| check is very important; without it, we'll
-      // renice aPid twice, and the second renice will be relative to the
-      // priority set by the first renice.)
-      continue;
-    }
-
-    int tid = static_cast<int>(tidlong);
-
-    // Do not set the priority of threads running with a real-time policy
-    // as part of the bulk process adjustment.  These threads need to run
-    // at their specified priority in order to meet timing guarantees.
-    int schedPolicy = sched_getscheduler(tid);
-    if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
-      continue;
-    }
-
-    errno = 0;
-    // Get and set the task's new priority.
-    int origtaskpriority = getpriority(PRIO_PROCESS, tid);
-    if (errno) {
-      HAL_LOG("Unable to get nice for tid=%d (pid=%d); error %d.  This isn't "
-              "necessarily a problem; it could be a benign race condition.",
-              tid, aPid, errno);
-      continue;
-    }
-
-    int newtaskpriority =
-      std::max(origtaskpriority - origProcPriority + aNice, aNice);
-
-    // Do not reduce priority of threads already running at priorities greater
-    // than normal.  These threads are likely special service threads that need
-    // elevated priorities to process audio, display composition, etc.
-    if (newtaskpriority > origtaskpriority &&
-        origtaskpriority < ANDROID_PRIORITY_NORMAL) {
-      continue;
-    }
-
-    rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
-
-    if (rv) {
-      HAL_LOG("Unable to set nice for tid=%d (pid=%d); error %d.  This isn't "
-              "necessarily a problem; it could be a benign race condition.",
-              tid, aPid, errno);
-      continue;
-    }
-  }
-
-  HAL_LOG("Changed nice for pid %d from %d to %d.",
-          aPid, origProcPriority, aNice);
-
-  closedir(tasksDir);
-}
-
-/*
- * Used to store the nice value adjustments and oom_adj values for the various
- * process priority levels.
- */
-struct ProcessPriorityPrefs {
-  bool initialized;
-  int lowPriorityNice;
-  struct {
-    int nice;
-    int oomScoreAdj;
-  } priorities[NUM_PROCESS_PRIORITY];
-};
-
-/*
- * Reads the preferences for the various process priority levels and sets up
- * watchers so that if they're dynamically changed the change is reflected on
- * the appropriate variables.
- */
-void
-EnsureProcessPriorityPrefs(ProcessPriorityPrefs* prefs)
-{
-  if (prefs->initialized) {
-    return;
-  }
-
-  // Read the preferences for process priority levels
-  for (int i = PROCESS_PRIORITY_BACKGROUND; i < NUM_PROCESS_PRIORITY; i++) {
-    ProcessPriority priority = static_cast<ProcessPriority>(i);
-
-    // Read the nice values
-    const char* processPriorityStr = ProcessPriorityToString(priority);
-    nsPrintfCString niceStr("hal.processPriorityManager.gonk.%s.Nice",
-                            processPriorityStr);
-    Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
-
-    // Read the oom_adj scores
-    nsPrintfCString oomStr("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
-                           processPriorityStr);
-    Preferences::AddIntVarCache(&prefs->priorities[i].oomScoreAdj,
-                                oomStr.get());
-  }
-
-  Preferences::AddIntVarCache(&prefs->lowPriorityNice,
-                              "hal.processPriorityManager.gonk.LowCPUNice");
-
-  prefs->initialized = true;
-}
-
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
                    ProcessCPUPriority aCPUPriority,
                    uint32_t aBackgroundLRU)
 {
   HAL_LOG("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)",
           aPid, aPriority, aCPUPriority, aBackgroundLRU);
@@ -1526,59 +1633,33 @@ SetProcessPriority(int aPid,
   // OOM parameters according to our prefs.
   //
   // We could/should do this on startup instead of waiting for the first
   // SetProcessPriorityCall.  But in practice, the master process needs to set
   // its priority early in the game, so we can reasonably rely on
   // SetProcessPriority being called early in startup.
   EnsureKernelLowMemKillerParamsSet();
 
-  static ProcessPriorityPrefs prefs = { 0 };
-  EnsureProcessPriorityPrefs(&prefs);
+  PriorityClass* pc = GetPriorityClass(aPriority);
 
-  int oomScoreAdj = prefs.priorities[aPriority].oomScoreAdj;
+  int oomScoreAdj = pc->OomScoreAdj();
 
   RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
 
-  int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
-                                                     OOM_SCORE_ADJ_MAX);
-  if (clampedOomScoreAdj != oomScoreAdj) {
-    HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
-            clampedOomScoreAdj);
-  } else {
-    HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
-            clampedOomScoreAdj);
-  }
-
   // We try the newer interface first, and fall back to the older interface
   // on failure.
-
   if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
-                   nsPrintfCString("%d", clampedOomScoreAdj).get()))
+                   nsPrintfCString("%d", oomScoreAdj).get()))
   {
-    int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
-
     WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
-                nsPrintfCString("%d", oomAdj).get());
+                nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
   }
 
-  int nice = 0;
-
-  if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
-    nice = prefs.priorities[aPriority].nice;
-  } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
-    nice = prefs.lowPriorityNice;
-  } else {
-    HAL_ERR("Unknown aCPUPriority value %d", aCPUPriority);
-    MOZ_ASSERT(false);
-    return;
-  }
-
-  HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
-  SetNiceForPid(aPid, nice);
+  HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
+  pc->AddProcess(aPid);
 }
 
 static bool
 IsValidRealTimePriority(int aValue, int aSchedulePolicy)
 {
   return (aValue >= sched_get_priority_min(aSchedulePolicy)) &&
          (aValue <= sched_get_priority_max(aSchedulePolicy));
 }