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 222872 59452425e446c374a486169652abfff985df6759
parent 222871 a13166f0d914a85a083e5e22fc057be76538f0d8
child 222873 2e983faaa3a5b7751b856e9fba4af91a4659534c
push id53740
push usercbook@mozilla.com
push dateFri, 09 Jan 2015 08:02:37 +0000
treeherdermozilla-inbound@5f8dbb495675 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands
bugs1081871
milestone37.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 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));
 }