Bug 822325: Implement an LRU pool for background app processes. The LRU app will get the smallest oom_adj and get killed last. r=khuey
authorAlan Huang <ahuang@mozilla.com>
Tue, 01 Oct 2013 11:54:59 +0800
changeset 151585 bca0c6bde0ef1de4091b4cfcc0d81726f028476f
parent 151584 0853c6fedfcbfd8fd15f48f142a8666449796c09
child 151586 67137bb90796d6d66ae1df79104ac841f67ef4a5
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-esr52@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs822325
milestone27.0a1
Bug 822325: Implement an LRU pool for background app processes. The LRU app will get the smallest oom_adj and get killed last. r=khuey
b2g/app/b2g.js
dom/browser-element/mochitest/browserElementTestHelpers.js
dom/browser-element/mochitest/priority/Makefile.in
dom/browser-element/mochitest/priority/test_BackgroundLRU.html
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/ProcessPriorityManager.h
hal/Hal.cpp
hal/Hal.h
hal/fallback/FallbackProcessPriority.cpp
hal/gonk/GonkHal.cpp
hal/sandbox/SandboxHal.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -598,16 +598,21 @@ pref("ui.showHideScrollbars", 1);
 
 // Enable the ProcessPriorityManager, and give processes with no visible
 // documents a 1s grace period before they're eligible to be marked as
 // background.
 pref("dom.ipc.processPriorityManager.enabled", true);
 pref("dom.ipc.processPriorityManager.backgroundGracePeriodMS", 1000);
 pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
 
+// 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
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -41,16 +41,17 @@ const browserElementTestHelpers = {
       this._firedTestReady = true;
       dispatchEvent(new Event("testready"));
     }
   },
 
   enableProcessPriorityManager: function() {
     this._setPref('dom.ipc.processPriorityManager.testMode', true);
     this._setPref('dom.ipc.processPriorityManager.enabled', true);
+    this._setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
   },
 
   setEnabledPref: function(value) {
     this._setPref('dom.mozBrowserFramesEnabled', value);
   },
 
   getOOPByDefaultPref: function() {
     return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
@@ -188,16 +189,47 @@ function expectPriorityChange(childID, e
         deferred.reject();
       }
     }
   );
 
   return deferred.promise;
 }
 
+// Returns a promise which is resolved or rejected the next time the background
+// process childID changes its priority.  We resolve if the backgroundLRU
+// matches expectedBackgroundLRU and we reject otherwise.
+
+function expectPriorityWithBackgroundLRUSet(childID, expectedBackgroundLRU) {
+  var deferred = Promise.defer();
+
+  browserElementTestHelpers.addProcessPriorityObserver(
+    'process-priority-with-background-LRU-set',
+    function(subject, topic, data) {
+
+      [id, priority, cpuPriority, backgroundLRU] = data.split(":");
+      if (id != childID) {
+        return;
+      }
+
+      is(backgroundLRU, expectedBackgroundLRU,
+         'Expected backgroundLRU ' + backgroundLRU + ' of childID ' + childID +
+         ' to change to ' + expectedBackgroundLRU);
+
+      if (backgroundLRU == expectedBackgroundLRU) {
+        deferred.resolve();
+      } else {
+        deferred.reject();
+      }
+    }
+  );
+
+  return deferred.promise;
+}
+
 // Returns a promise which is resolved the first time the given iframe fires
 // the mozbrowser##eventName event.
 function expectMozbrowserEvent(iframe, eventName) {
   var deferred = Promise.defer();
   iframe.addEventListener('mozbrowser' + eventName, function handler(e) {
     iframe.removeEventListener('mozbrowser' + eventName, handler);
     deferred.resolve(e);
   });
--- a/dom/browser-element/mochitest/priority/Makefile.in
+++ b/dom/browser-element/mochitest/priority/Makefile.in
@@ -18,16 +18,17 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 MOCHITEST_FILES = \
 	test_Simple.html \
 	test_Visibility.html \
 	test_HighPriority.html \
 	file_HighPriority.html \
 	test_HighPriorityDowngrade.html \
 	test_HighPriorityDowngrade2.html \
 	test_Background.html \
+	test_BackgroundLRU.html \
 	test_Audio.html \
 	file_Audio.html \
 	silence.ogg \
 	test_MultipleFrames.html \
 	file_MultipleFrames.html \
 	test_Preallocated.html \
 	test_ExpectingSystemMessage.html \
 	test_ExpectingSystemMessage2.html \
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/priority/test_BackgroundLRU.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that calling setVisible('false') on two iframes causes the former one's priority with background LRU to
+change.
+-->
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+browserElementTestHelpers.enableProcessPriorityManager();
+SpecialPowers.addPermission("embed-apps", true, document);
+
+function runTest() {
+  var iframe1 = document.createElement('iframe');
+  iframe1.setAttribute('mozbrowser', true);
+  iframe1.src = 'file_MultipleFrames.html';
+
+  var iframe2 = null;
+  var childID = null;
+
+  expectProcessCreated().then(function(chid) {
+    childID = chid;
+    return expectPriorityChange(childID, 'FOREGROUND');
+  }).then(function() {
+    return expectMozbrowserEvent(iframe1, 'openwindow');
+  }).then(function() {
+    var p = expectPriorityChange(childID, 'BACKGROUND');
+    iframe1.setVisible(false);
+    return p;
+  }).then(function() {
+    iframe2 = document.createElement('iframe');
+    iframe2.setAttribute('mozbrowser', true);
+    iframe2.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+    iframe2.src = browserElementTestHelpers.emptyPage1;
+
+    document.body.appendChild(iframe2);
+
+    // At this point, we should have iframe1 in background already.
+    // We wait until another one is set to background, too.
+    // Once there are two in background, the first one (LRU order)
+    // should have 'backgroundLRU' equals 1
+    var p = expectPriorityWithBackgroundLRUSet(childID, '1');
+    iframe2.setVisible(false);
+    document.body.removeChild(iframe2);
+
+    return p;
+
+  }).then(SimpleTest.finish);
+
+  document.body.appendChild(iframe1);
+}
+
+addEventListener('testready', runTest);
+
+</script>
+</body>
+</html>
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -121,17 +121,18 @@ public:
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /**
    * This function implements ProcessPriorityManager::SetProcessPriority.
    */
   void SetProcessPriority(ContentParent* aContentParent,
-                          ProcessPriority aPriority);
+                          ProcessPriority aPriority,
+                          uint32_t aBackgroundLRU = 0);
 
   /**
    * If a magic testing-only pref is set, notify the observer service on the
    * given topic with the given data.  This is used for testing
    */
   void FireTestOnlyObserverNotification(const char* aTopic,
                                         const nsACString& aData = EmptyCString());
 
@@ -221,16 +222,17 @@ public:
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
 
   virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE;
   void Init();
 
   int32_t Pid() const;
   uint64_t ChildID() const;
+  bool IsPreallocated() const;
 
   /**
    * Used in logging, this method returns the ContentParent's name followed by
    * ", ".  If we can't get the ContentParent's name for some reason, it
    * returns an empty string.
    *
    * The reference returned here is guaranteed to be live until the next call
    * to NameWithComma() or until the ParticularProcessPriorityManager is
@@ -255,20 +257,22 @@ public:
   void ResetPriority();
   void ResetPriorityNow();
   void ResetCPUPriorityNow();
 
   /**
    * This overload is equivalent to SetPriorityNow(aPriority,
    * ComputeCPUPriority()).
    */
-  void SetPriorityNow(ProcessPriority aPriority);
+  void SetPriorityNow(ProcessPriority aPriority,
+                      uint32_t aBackgroundLRU = 0);
 
   void SetPriorityNow(ProcessPriority aPriority,
-                      ProcessCPUPriority aCPUPriority);
+                      ProcessCPUPriority aCPUPriority,
+                      uint32_t aBackgroundLRU = 0);
 
   void ShutDown();
 
 private:
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const nsACString& aData = EmptyCString());
 
@@ -286,16 +290,56 @@ private:
   /**
    * Used to implement NameWithComma().
    */
   nsAutoCString mNameWithComma;
 
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 };
 
+class BackgroundProcessLRUPool MOZ_FINAL
+{
+public:
+  static BackgroundProcessLRUPool* Singleton();
+
+  /**
+   * Used to remove a ContentParent from background LRU pool when
+   * it is destroyed or its priority changed from BACKGROUND to others.
+   */
+  void RemoveFromBackgroundLRUPool(ContentParent* aContentParent);
+
+  /**
+   * Used to add a ContentParent into background LRU pool when
+   * its priority changed to BACKGROUND from others.
+   */
+  void AddIntoBackgroundLRUPool(ContentParent* aContentParent);
+
+private:
+  static StaticAutoPtr<BackgroundProcessLRUPool> sSingleton;
+
+  int32_t mLRUPoolLevels;
+  int32_t mLRUPoolSize;
+  int32_t mLRUPoolAvailableIndex;
+  nsTArray<ContentParent*> mLRUPool;
+
+  uint32_t CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex);
+
+  nsresult UpdateAvailableIndexInLRUPool(
+      ContentParent* aContentParent,
+      int32_t aTargetIndex = -1);
+
+  void ShiftLRUPool();
+
+  void EnsureLRUPool();
+
+  BackgroundProcessLRUPool();
+  DISALLOW_EVIL_CONSTRUCTORS(BackgroundProcessLRUPool);
+
+};
+
 /* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
 /* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
 /* static */ StaticRefPtr<ProcessPriorityManagerImpl>
   ProcessPriorityManagerImpl::sSingleton;
 
 NS_IMPL_ISUPPORTS1(ProcessPriorityManagerImpl,
                    nsIObserver);
 
@@ -416,22 +460,23 @@ ProcessPriorityManagerImpl::GetParticula
       nsPrintfCString("%lld", aContentParent->ChildID()));
   }
 
   return pppm.forget();
 }
 
 void
 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
-                                               ProcessPriority aPriority)
+                                               ProcessPriority aPriority,
+                                               uint32_t aBackgroundLRU)
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
-  pppm->SetPriorityNow(aPriority);
+  pppm->SetPriorityNow(aPriority, aBackgroundLRU);
 }
 
 void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
@@ -652,16 +697,22 @@ ParticularProcessPriorityManager::ChildI
 }
 
 int32_t
 ParticularProcessPriorityManager::Pid() const
 {
   return mContentParent ? mContentParent->Pid() : -1;
 }
 
+bool
+ParticularProcessPriorityManager::IsPreallocated() const
+{
+  return mContentParent ? mContentParent->IsPreallocated() : false;
+}
+
 const nsAutoCString&
 ParticularProcessPriorityManager::NameWithComma()
 {
   mNameWithComma.Truncate();
   if (!mContentParent) {
     return mNameWithComma; // empty string
   }
 
@@ -891,44 +942,71 @@ ParticularProcessPriorityManager::Comput
 
 void
 ParticularProcessPriorityManager::ResetCPUPriorityNow()
 {
   SetPriorityNow(mPriority);
 }
 
 void
-ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
+ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
+                                                 uint32_t aBackgroundLRU)
 {
-  SetPriorityNow(aPriority, ComputeCPUPriority());
+  SetPriorityNow(aPriority, ComputeCPUPriority(), aBackgroundLRU);
 }
 
 void
 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
-                                                 ProcessCPUPriority aCPUPriority)
+                                                 ProcessCPUPriority aCPUPriority,
+                                                 uint32_t aBackgroundLRU)
 {
   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     MOZ_ASSERT(false);
     return;
   }
 
+  if (aBackgroundLRU > 0 &&
+      aPriority == PROCESS_PRIORITY_BACKGROUND &&
+      mPriority == PROCESS_PRIORITY_BACKGROUND) {
+    hal::SetProcessPriority(Pid(), mPriority, mCPUPriority, aBackgroundLRU);
+
+    nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d",
+      ProcessPriorityToString(mPriority, mCPUPriority),
+      aBackgroundLRU);
+
+    FireTestOnlyObserverNotification("process-priority-with-background-LRU-set",
+      ProcessPriorityWithBackgroundLRU.get());
+  }
+
   if (!mContentParent ||
       !ProcessPriorityManagerImpl::PrefsEnabled() ||
       (mPriority == aPriority && mCPUPriority == aCPUPriority)) {
     return;
   }
 
   // If the prefs were disabled after this ParticularProcessPriorityManager was
   // created, we can at least avoid any further calls to
   // hal::SetProcessPriority.  Supporting dynamic enabling/disabling of the
   // ProcessPriorityManager is mostly for testing.
   if (!ProcessPriorityManagerImpl::PrefsEnabled()) {
     return;
   }
 
+  if (aPriority == PROCESS_PRIORITY_BACKGROUND &&
+      mPriority != PROCESS_PRIORITY_BACKGROUND &&
+      !IsPreallocated()) {
+    ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent);
+  }
+
+  if (aPriority != PROCESS_PRIORITY_BACKGROUND &&
+      mPriority == PROCESS_PRIORITY_BACKGROUND &&
+      !IsPreallocated()) {
+    ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
+  }
+
   LOGP("Changing priority from %s to %s.",
        ProcessPriorityToString(mPriority, mCPUPriority),
        ProcessPriorityToString(aPriority, aCPUPriority));
 
   ProcessPriority oldPriority = mPriority;
 
   mPriority = aPriority;
   mCPUPriority = aCPUPriority;
@@ -939,16 +1017,23 @@ ParticularProcessPriorityManager::SetPri
   }
 
   if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
     unused << mContentParent->SendCancelMinimizeMemoryUsage();
   } else {
     unused << mContentParent->SendMinimizeMemoryUsage();
   }
 
+  nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d",
+    ProcessPriorityToString(mPriority, mCPUPriority),
+    aBackgroundLRU);
+
+  FireTestOnlyObserverNotification("process-priority-with-background-LRU-set",
+    ProcessPriorityWithBackgroundLRU.get());
+
   FireTestOnlyObserverNotification("process-priority-set",
     ProcessPriorityToString(mPriority, mCPUPriority));
 
   if (oldPriority != mPriority) {
     ProcessPriorityManagerImpl::GetSingleton()->
       NotifyProcessPriorityChanged(this, oldPriority);
   }
 }
@@ -960,16 +1045,18 @@ ParticularProcessPriorityManager::ShutDo
 
   UnregisterWakeLockObserver(this);
 
   if (mResetPriorityTimer) {
     mResetPriorityTimer->Cancel();
     mResetPriorityTimer = nullptr;
   }
 
+  ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
+
   mContentParent = nullptr;
 }
 
 void
 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
   const char* aTopic,
   const nsACString& aData /* = EmptyCString() */)
 {
@@ -1093,16 +1180,191 @@ ProcessPriorityManagerChild::Observe(
 
 bool
 ProcessPriorityManagerChild::CurrentProcessIsForeground()
 {
   return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
          mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
 }
 
+/* static */ StaticAutoPtr<BackgroundProcessLRUPool>
+BackgroundProcessLRUPool::sSingleton;
+
+/* static */ BackgroundProcessLRUPool*
+BackgroundProcessLRUPool::Singleton()
+{
+  if (!sSingleton) {
+    sSingleton = new BackgroundProcessLRUPool();
+    ClearOnShutdown(&sSingleton);
+  }
+  return sSingleton;
+}
+
+BackgroundProcessLRUPool::BackgroundProcessLRUPool()
+{
+  EnsureLRUPool();
+}
+
+uint32_t
+BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex)
+{
+  // Set LRU level of each background process and maintain LRU buffer as below:
+
+  // Priority background  : LRU0
+  // Priority background+1: LRU1, LRU2
+  // Priority background+2: LRU3, LRU4, LRU5, LRU6
+  // Priority background+3: LRU7, LRU8, LRU9, LRU10, LRU11, LRU12, LRU13, LRU14
+  // ...
+  // Priority background+L-1: 2^(number of background LRU pool levels - 1)
+  // (End of buffer)
+
+  return (uint32_t)(log((float)aBackgroundLRUPoolIndex) / log(2.0));
+}
+
+void
+BackgroundProcessLRUPool::EnsureLRUPool()
+{
+  // We set mBackgroundLRUPoolLevels according to our pref.
+  // This value is used to set background process LRU pool
+  if (!NS_SUCCEEDED(Preferences::GetInt(
+        "dom.ipc.processPriorityManager.backgroundLRUPoolLevels",
+        &mLRUPoolLevels))) {
+    mLRUPoolLevels = 1;
+  }
+
+  if (mLRUPoolLevels <= 0) {
+    MOZ_CRASH();
+  }
+
+  // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
+  // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
+  // This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
+  // See bug 822325 comment 49
+  MOZ_ASSERT(mLRUPoolLevels <= 6);
+
+  // LRU pool size = 2 ^ (number of background LRU pool levels) - 1
+  mLRUPoolSize = (1 << mLRUPoolLevels) - 1;
+
+  mLRUPoolAvailableIndex = 0;
+
+  LOG("Making background LRU pool with size(%d)", mLRUPoolSize);
+
+  mLRUPool.InsertElementsAt(0, mLRUPoolSize, (ContentParent*)nullptr);
+}
+
+void
+BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool(
+    ContentParent* aContentParent)
+{
+  for (int32_t i = 0; i < mLRUPoolSize; i++) {
+    if (mLRUPool[i]) {
+      if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
+
+        mLRUPool[i] = nullptr;
+        LOG("Remove ChildID(%llu) from LRU pool", aContentParent->ChildID());
+
+        // After we remove this ContentParent from LRU pool, we still need to
+        // update the available index if the index of removed one is less than
+        // the available index we already have.
+        UpdateAvailableIndexInLRUPool(aContentParent, i);
+        break;
+      }
+    }
+  }
+}
+
+nsresult
+BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool(
+    ContentParent* aContentParent,
+    int32_t aTargetIndex)
+{
+  // If we specify which index we want to assign to mLRUPoolAvailableIndex,
+  // We have to make sure the index in LRUPool doesn't point to any
+  // ContentParent.
+  if (aTargetIndex >= 0 && aTargetIndex < mLRUPoolSize &&
+      aTargetIndex < mLRUPoolAvailableIndex &&
+      !mLRUPool[aTargetIndex]) {
+    mLRUPoolAvailableIndex = aTargetIndex;
+    return NS_OK;
+  }
+
+  // When we didn't specify any legal aTargetIndex, then we just check
+  // whether current mLRUPoolAvailableIndex points to any ContentParent or not.
+  if (mLRUPoolAvailableIndex >= 0 && mLRUPoolAvailableIndex < mLRUPoolSize &&
+      !(mLRUPool[mLRUPoolAvailableIndex])) {
+    return NS_OK;
+  }
+
+  // Both above way failed. So now we have to find proper value
+  // for mLRUPoolAvailableIndex.
+  // We are looking for an available index. We only shift process with
+  // LRU less than the available index should have, so we stop update
+  // mLRUPoolAvailableIndex from the for loop once we got a candidate.
+  mLRUPoolAvailableIndex = -1;
+
+  for (int32_t i = 0; i < mLRUPoolSize; i++) {
+    if (mLRUPool[i]) {
+      if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
+        LOG("ChildID(%llu) already in LRU pool", aContentParent->ChildID());
+        MOZ_ASSERT(false);
+        return NS_ERROR_UNEXPECTED;
+      }
+      continue;
+    } else {
+      if (mLRUPoolAvailableIndex == -1) {
+        mLRUPoolAvailableIndex = i;
+      }
+    }
+  }
+
+  // If the LRUPool is already full, mLRUPoolAvailableIndex is still -1 after
+  // above loop finished. We should set mLRUPoolAvailableIndex
+  // to mLRUPoolSize - 1 in this case. Here uses the mod operator to do it:
+  // New mLRUPoolAvailableIndex either equals old mLRUPoolAvailableIndex, or
+  // mLRUPoolSize - 1 if old mLRUPoolAvailableIndex is -1.
+  mLRUPoolAvailableIndex =
+    (mLRUPoolAvailableIndex + mLRUPoolSize) % mLRUPoolSize;
+
+  return NS_OK;
+}
+
+void
+BackgroundProcessLRUPool::ShiftLRUPool()
+{
+  for (int32_t i = mLRUPoolAvailableIndex; i > 0; i--) {
+    mLRUPool[i] = mLRUPool[i - 1];
+    // Check whether i+1 is power of Two.
+    // If so, then it crossed a LRU group boundary and
+    // we need to assign its new process priority LRU.
+    if (!((i + 1) & i)) {
+      ProcessPriorityManagerImpl::GetSingleton()->SetProcessPriority(
+        mLRUPool[i], PROCESS_PRIORITY_BACKGROUND, CalculateLRULevel(i + 1));
+    }
+  }
+}
+
+void
+BackgroundProcessLRUPool::AddIntoBackgroundLRUPool(
+    ContentParent* aContentParent)
+{
+  // We have to make sure that we have correct available index in LRU pool
+  if (!NS_SUCCEEDED(
+      UpdateAvailableIndexInLRUPool(aContentParent))) {
+    return;
+  }
+
+  // Shift the list in the pool, so we have room at index 0 for the newly added
+  // ContentParent
+  ShiftLRUPool();
+
+  mLRUPool[0] = aContentParent;
+
+  LOG("Add ChildID(%llu) into LRU pool", aContentParent->ChildID());
+}
+
 } // anonymous namespace
 
 namespace mozilla {
 
 /* static */ void
 ProcessPriorityManager::Init()
 {
   ProcessPriorityManagerImpl::StaticInit();
@@ -1117,16 +1379,41 @@ ProcessPriorityManager::SetProcessPriori
 
   ProcessPriorityManagerImpl* singleton =
     ProcessPriorityManagerImpl::GetSingleton();
   if (singleton) {
     singleton->SetProcessPriority(aContentParent, aPriority);
   }
 }
 
+/* static */ void
+ProcessPriorityManager::RemoveFromBackgroundLRUPool(
+    ContentParent* aContentParent)
+{
+  MOZ_ASSERT(aContentParent);
+
+  BackgroundProcessLRUPool* singleton =
+    BackgroundProcessLRUPool::Singleton();
+  if (singleton) {
+    singleton->RemoveFromBackgroundLRUPool(aContentParent);
+  }
+}
+
+/* static */ void
+ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent)
+{
+  MOZ_ASSERT(aContentParent);
+
+  BackgroundProcessLRUPool* singleton =
+    BackgroundProcessLRUPool::Singleton();
+  if (singleton) {
+    singleton->AddIntoBackgroundLRUPool(aContentParent);
+  }
+}
+
 /* static */ bool
 ProcessPriorityManager::CurrentProcessIsForeground()
 {
   return ProcessPriorityManagerChild::Singleton()->
     CurrentProcessIsForeground();
 }
 
 } // namespace mozilla
--- a/dom/ipc/ProcessPriorityManager.h
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -63,16 +63,28 @@ public:
    * Returns true iff this process's priority is FOREGROUND*.
    *
    * Note that because process priorities are set in the main process, it's
    * possible for this method to return a stale value.  So be careful about
    * what you use this for.
    */
   static bool CurrentProcessIsForeground();
 
+  /**
+   * Used to remove a ContentParent from background LRU pool when
+   * it is destroyed or its priority changed from BACKGROUND to others.
+   */
+  static void RemoveFromBackgroundLRUPool(dom::ContentParent* aContentParent);
+
+  /**
+   * Used to add a ContentParent into background LRU pool when
+   * its priority changed to BACKGROUND from others.
+   */
+  static void AddIntoBackgroundLRUPool(dom::ContentParent* aContentParent);
+
 private:
   ProcessPriorityManager();
   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager);
 };
 
 } // namespace mozilla
 
 #endif
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -845,21 +845,24 @@ SetAlarm(int32_t aSeconds, int32_t aNano
   // It's pointless to program an alarm nothing is going to observe ...
   MOZ_ASSERT(sAlarmObserver);
   RETURN_PROXY_IF_SANDBOXED(SetAlarm(aSeconds, aNanoseconds), false);
 }
 
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
-                   ProcessCPUPriority aCPUPriority)
+                   ProcessCPUPriority aCPUPriority,
+                   uint32_t aBackgroundLRU)
 {
   // n.b. The sandboxed implementation crashes; SetProcessPriority works only
   // from the main process.
-  PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aCPUPriority));
+  MOZ_ASSERT(aBackgroundLRU == 0 || aPriority == PROCESS_PRIORITY_BACKGROUND);
+  PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aCPUPriority,
+                                        aBackgroundLRU));
 }
 
 // From HalTypes.h.
 const char*
 ProcessPriorityToString(ProcessPriority aPriority)
 {
   switch (aPriority) {
   case PROCESS_PRIORITY_MASTER:
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -478,17 +478,18 @@ bool SetAlarm(int32_t aSeconds, int32_t 
  * of its ProcessPriority and ProcessCPUPriority.
  *
  * Exactly what this does will vary between platforms.  On *nix we might give
  * background processes higher nice values.  On other platforms, we might
  * ignore this call entirely.
  */
 void SetProcessPriority(int aPid,
                         hal::ProcessPriority aPriority,
-                        hal::ProcessCPUPriority aCPUPriority);
+                        hal::ProcessCPUPriority aCPUPriority,
+                        uint32_t aLRU = 0);
 
 /**
  * Register an observer for the FM radio.
  */
 void RegisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
 
 /**
  * Unregister the observer for the FM radio.
--- a/hal/fallback/FallbackProcessPriority.cpp
+++ b/hal/fallback/FallbackProcessPriority.cpp
@@ -7,16 +7,18 @@
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace hal_impl {
 
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
-                   ProcessCPUPriority aCPUPriority)
+                   ProcessCPUPriority aCPUPriority,
+                   uint32_t aBackgroundLRU)
 {
-  HAL_LOG(("FallbackProcessPriority - SetProcessPriority(%d, %s)\n",
-           aPid, ProcessPriorityToString(aPriority, aCPUPriority)));
+  HAL_LOG(("FallbackProcessPriority - SetProcessPriority(%d, %s, %u)\n",
+           aPid, ProcessPriorityToString(aPriority, aCPUPriority),
+           aBackgroundLRU));
 }
 
 } // hal_impl
 } // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -1025,16 +1025,25 @@ OomAdjOfOomScoreAdj(int aOomScoreAdj)
   } else {
     adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX;
   }
 
   return adj;
 }
 
 static void
+RoundOomScoreAdjUpWithBackroundLRU(int& aOomScoreAdj, uint32_t aBackgroundLRU)
+{
+  // We want to add minimum value to round OomScoreAdj up according to
+  // the steps by aBackgroundLRU.
+  aOomScoreAdj +=
+    ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aBackgroundLRU);
+}
+
+static void
 EnsureKernelLowMemKillerParamsSet()
 {
   static bool kernelLowMemKillerParamsSet;
   if (kernelLowMemKillerParamsSet) {
     return;
   }
   kernelLowMemKillerParamsSet = true;
 
@@ -1199,35 +1208,38 @@ SetNiceForPid(int aPid, int aNice)
       aPid, origProcPriority, aNice);
 
   closedir(tasksDir);
 }
 
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
-                   ProcessCPUPriority aCPUPriority)
+                   ProcessCPUPriority aCPUPriority,
+                   uint32_t aBackgroundLRU)
 {
-  HAL_LOG(("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d)",
-           aPid, aPriority, aCPUPriority));
+  HAL_LOG(("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)",
+           aPid, aPriority, aCPUPriority, aBackgroundLRU));
 
   // If this is the first time SetProcessPriority was called, set the kernel's
   // 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();
 
   int32_t oomScoreAdj = 0;
   nsresult rv = Preferences::GetInt(nsPrintfCString(
     "hal.processPriorityManager.gonk.%s.OomScoreAdjust",
     ProcessPriorityToString(aPriority)).get(), &oomScoreAdj);
 
+  RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
+
   if (NS_SUCCEEDED(rv)) {
     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",
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -337,17 +337,18 @@ SetAlarm(int32_t aSeconds, int32_t aNano
 {
   NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts.  Yet.");
   return false;
 }
 
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
-                   ProcessCPUPriority aCPUPriority)
+                   ProcessCPUPriority aCPUPriority,
+                   uint32_t aBackgroundLRU)
 {
   NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
 }
 
 void
 EnableFMRadio(const hal::FMRadioSettings& aSettings)
 {
   NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts.");