API to query which cores are exclusively assigned.
On devices with cpuset support, the foreground app
may have a core exclusively assigned to it. Add an
API that allows apps to query which core that is,
so it can use that to set scheduling affinity.
Bug: 27381794
Change-Id: Iea148776da6d7abb745ccf9e6ad5ad46491e340a
diff --git a/api/current.txt b/api/current.txt
index 07505b3..e03dcd2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29046,6 +29046,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 089ccd3..d4c33ca 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -31338,6 +31338,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 21a9db0..b0640f2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -29057,6 +29057,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9984755..38d5b34 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -975,6 +975,31 @@
throws IllegalArgumentException, SecurityException;
/**
+ * On some devices, the foreground process may have one or more CPU
+ * cores exclusively reserved for it. This method can be used to
+ * retrieve which cores that are (if any), so the calling process
+ * can then use sched_setaffinity() to lock a thread to these cores.
+ * Note that the calling process must currently be running in the
+ * foreground for this method to return any cores.
+ *
+ * The CPU core(s) exclusively reserved for the foreground process will
+ * stay reserved for as long as the process stays in the foreground.
+ *
+ * As soon as a process leaves the foreground, those CPU cores will
+ * no longer be reserved for it, and will most likely be reserved for
+ * the new foreground process. It's not necessary to change the affinity
+ * of your process when it leaves the foreground (if you had previously
+ * set it to use a reserved core); the OS will automatically take care
+ * of resetting the affinity at that point.
+ *
+ * @return an array of integers, indicating the CPU cores exclusively
+ * reserved for this process. The array will have length zero if no
+ * CPU cores are exclusively reserved for this process at this point
+ * in time.
+ */
+ public static final native int[] getExclusiveCores();
+
+ /**
* Set the priority of the calling thread, based on Linux priorities. See
* {@link #setThreadPriority(int, int)} for more information.
*
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index ee8fb19..f7a5e8a 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "Process"
+// To make sure cpu_set_t is included from sched.h
+#define _GNU_SOURCE 1
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -288,6 +290,139 @@
return (int) sp;
}
+#ifdef ENABLE_CPUSETS
+/** Sample CPUset list format:
+ * 0-3,4,6-8
+ */
+static void parse_cpuset_cpus(char *cpus, cpu_set_t *cpu_set) {
+ unsigned int start, end, matched, i;
+ char *cpu_range = strtok(cpus, ",");
+ while (cpu_range != NULL) {
+ start = end = 0;
+ matched = sscanf(cpu_range, "%u-%u", &start, &end);
+ cpu_range = strtok(NULL, ",");
+ if (start >= CPU_SETSIZE) {
+ ALOGE("parse_cpuset_cpus: ignoring CPU number larger than %d.", CPU_SETSIZE);
+ continue;
+ } else if (end >= CPU_SETSIZE) {
+ ALOGE("parse_cpuset_cpus: ignoring CPU numbers larger than %d.", CPU_SETSIZE);
+ end = CPU_SETSIZE - 1;
+ }
+ if (matched == 1) {
+ CPU_SET(start, cpu_set);
+ } else if (matched == 2) {
+ for (i = start; i <= end; i++) {
+ CPU_SET(i, cpu_set);
+ }
+ } else {
+ ALOGE("Failed to match cpus");
+ }
+ }
+ return;
+}
+
+/**
+ * Stores the CPUs assigned to the cpuset corresponding to the
+ * SchedPolicy in the passed in cpu_set.
+ */
+static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set)
+{
+ FILE *file;
+ const char *filename;
+
+ CPU_ZERO(cpu_set);
+
+ switch (policy) {
+ case SP_BACKGROUND:
+ filename = "/dev/cpuset/background/cpus";
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ filename = "/dev/cpuset/foreground/cpus";
+ break;
+ case SP_TOP_APP:
+ filename = "/dev/cpuset/top-app/cpus";
+ break;
+ default:
+ filename = NULL;
+ }
+
+ if (!filename) return;
+
+ file = fopen(filename, "re");
+ if (file != NULL) {
+ // Parse cpus string
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t num_read = getline(&line, &len, file);
+ fclose (file);
+ if (num_read > 0) {
+ parse_cpuset_cpus(line, cpu_set);
+ } else {
+ ALOGE("Failed to read %s", filename);
+ }
+ free(line);
+ }
+ return;
+}
+#endif
+
+
+/**
+ * Determine CPU cores exclusively assigned to the
+ * cpuset corresponding to the SchedPolicy and store
+ * them in the passed in cpu_set_t
+ */
+void get_exclusive_cpuset_cores(SchedPolicy policy, cpu_set_t *cpu_set) {
+#ifdef ENABLE_CPUSETS
+ int i;
+ cpu_set_t tmp_set;
+ get_cpuset_cores_for_policy(policy, cpu_set);
+ for (i = 0; i < SP_CNT; i++) {
+ if ((SchedPolicy) i == policy) continue;
+ get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
+ // First get cores exclusive to one set or the other
+ CPU_XOR(&tmp_set, cpu_set, &tmp_set);
+ // Then get the ones only in cpu_set
+ CPU_AND(cpu_set, cpu_set, &tmp_set);
+ }
+#else
+ (void) policy;
+ CPU_ZERO(cpu_set);
+#endif
+ return;
+}
+
+jintArray android_os_Process_getExclusiveCores(JNIEnv* env, jobject clazz) {
+ SchedPolicy sp;
+ cpu_set_t cpu_set;
+ jintArray cpus;
+ int pid = getpid();
+ if (get_sched_policy(pid, &sp) != 0) {
+ signalExceptionForGroupError(env, errno);
+ return NULL;
+ }
+ get_exclusive_cpuset_cores(sp, &cpu_set);
+ int num_cpus = CPU_COUNT(&cpu_set);
+ cpus = env->NewIntArray(num_cpus);
+ if (cpus == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jint* cpu_elements = env->GetIntArrayElements(cpus, 0);
+ int count = 0;
+ for (int i = 0; i < CPU_SETSIZE && count < num_cpus; i++) {
+ if (CPU_ISSET(i, &cpu_set)) {
+ cpu_elements[count++] = i;
+ }
+ }
+
+ env->ReleaseIntArrayElements(cpus, cpu_elements, 0);
+ return cpus;
+}
+
static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) {
// Establishes the calling thread as illegal to put into the background.
// Typically used only for the system process's main looper.
@@ -1053,6 +1188,7 @@
{"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
{"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup},
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
+ {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
{"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
{"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},