vm: Add support for changing the scheduler group for a thread.
Also temporarily bumps the scheduler group for background threads while performing GC

Signed-off-by: San Mehat <san@google.com>
diff --git a/vm/Thread.c b/vm/Thread.c
index 42b527e..680755c 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -43,6 +43,9 @@
 #undef __KERNEL__
 #endif
 
+// Change this to enable logging on cgroup errors
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
 // change this to LOGV/LOGD to debug thread activity
 #define LOG_THREAD  LOGVV
 
@@ -2745,6 +2748,41 @@
 };
 
 /*
+ * Change the scheduler cgroup of a pid
+ */
+int dvmChangeThreadSchedulerGroup(const char *cgroup)
+{
+#ifdef HAVE_ANDROID_OS
+    FILE *fp;
+    char path[255];
+    int rc;
+
+    sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup ? cgroup : ""));
+
+    if (!(fp = fopen(path, "w"))) {
+#if ENABLE_CGROUP_ERR_LOGGING
+        LOGW("Unable to open %s (%s)\n", path, strerror(errno));
+#endif
+        return -errno;
+    }
+
+    rc = fprintf(fp, "0");
+    fclose(fp);
+
+    if (rc < 0) {
+#if ENABLE_CGROUP_ERR_LOGGING
+        LOGW("Unable to move pid %d to cgroup %s (%s)\n", getpid(),
+             (cgroup ? cgroup : "<default>"), strerror(errno));
+#endif
+    }
+
+    return (rc < 0) ? errno : 0;
+#else // HAVE_ANDROID_OS
+    return 0;
+#endif
+}
+
+/*
  * Change the priority of a system thread to match that of the Thread object.
  *
  * We map a priority value from 1-10 to Linux "nice" values, where lower
@@ -2761,6 +2799,12 @@
     }
     newNice = kNiceValues[newPriority-1];
 
+    if (newPriority == ANDROID_PRIORITY_BACKGROUND) {
+        dvmChangeThreadSchedulerGroup("bg_non_interactive");
+    } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+        dvmChangeThreadSchedulerGroup(NULL);
+    }
+
     if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
         char* str = dvmGetThreadName(thread);
         LOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s\n",
diff --git a/vm/Thread.h b/vm/Thread.h
index b64f9b7..86a7845 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -398,6 +398,11 @@
 INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;}
 
 /*
+ * Change the scheduler group of the current process
+ */
+int dvmChangeThreadSchedulerGroup(const char *group);
+
+/*
  * Update the priority value of the underlying pthread.
  */
 void dvmChangeThreadPriority(Thread* thread, int newPriority);
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 6c1e680..09954ea 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -774,6 +774,11 @@
         /* Current value is numerically greater than "normal", which
          * in backward UNIX terms means lower priority.
          */
+
+        if (priorityResult == ANDROID_PRIORITY_BACKGROUND) {
+            dvmChangeThreadSchedulerGroup(NULL);
+        }
+
         if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
             LOGI_HEAP("Unable to elevate priority from %d to %d\n",
                 priorityResult, ANDROID_PRIORITY_NORMAL);
@@ -1019,6 +1024,10 @@
         } else {
             LOGD_HEAP("Reset priority to %d\n", oldThreadPriority);
         }
+
+        if (oldThreadPriority == ANDROID_PRIORITY_BACKGROUND) {
+            dvmChangeThreadSchedulerGroup("bg_non_interactive");
+        }
     }
     gcElapsedTime = (dvmGetRelativeTimeUsec() - gcHeap->gcStartTime) / 1000;
     if (gcElapsedTime < 10000) {