summaryrefslogtreecommitdiffstats
path: root/libcutils
diff options
context:
space:
mode:
Diffstat (limited to 'libcutils')
-rw-r--r--libcutils/android_reboot.c187
-rw-r--r--libcutils/ashmem-dev.c184
-rw-r--r--libcutils/ashmem-host.c7
-rw-r--r--libcutils/sched_policy.c135
4 files changed, 433 insertions, 80 deletions
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 6ae23c1..af7e189 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -14,43 +14,108 @@
* limitations under the License.
*/
-#include <unistd.h>
-#include <sys/reboot.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
+#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/list.h>
-#define UNUSED __attribute__((unused))
+#define TAG "android_reboot"
+#define READONLY_CHECK_MS 5000
+#define READONLY_CHECK_TIMES 50
-/* Check to see if /proc/mounts contains any writeable filesystems
- * backed by a block device.
- * Return true if none found, else return false.
+typedef struct {
+ struct listnode list;
+ struct mntent entry;
+} mntent_list;
+
+static bool has_mount_option(const char* opts, const char* opt_to_find)
+{
+ bool ret = false;
+ char* copy = NULL;
+ char* opt;
+ char* rem;
+
+ while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
+ if (!strcmp(opt, opt_to_find)) {
+ ret = true;
+ break;
+ }
+ }
+
+ free(copy);
+ return ret;
+}
+
+static bool is_block_device(const char* fsname)
+{
+ return !strncmp(fsname, "/dev/block", 10);
+}
+
+/* Find all read+write block devices in /proc/mounts and add them to
+ * |rw_entries|.
*/
-static int remount_ro_done(void)
+static void find_rw(struct listnode* rw_entries)
{
FILE* fp;
struct mntent* mentry;
- int found_rw_fs = 0;
if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
- /* If we can't read /proc/mounts, just give up. */
- return 1;
+ KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+ return;
}
while ((mentry = getmntent(fp)) != NULL) {
- if (!strncmp(mentry->mnt_fsname, "/dev/block", 10) && strstr(mentry->mnt_opts, "rw,")) {
- found_rw_fs = 1;
- break;
+ if (is_block_device(mentry->mnt_fsname) &&
+ has_mount_option(mentry->mnt_opts, "rw")) {
+ mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
+ item->entry = *mentry;
+ item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
+ item->entry.mnt_dir = strdup(mentry->mnt_dir);
+ item->entry.mnt_type = strdup(mentry->mnt_type);
+ item->entry.mnt_opts = strdup(mentry->mnt_opts);
+ list_add_tail(rw_entries, &item->list);
}
}
endmntent(fp);
+}
+
+static void free_entries(struct listnode* entries)
+{
+ struct listnode* node;
+ struct listnode* n;
+ list_for_each_safe(node, n, entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ free(item->entry.mnt_fsname);
+ free(item->entry.mnt_dir);
+ free(item->entry.mnt_type);
+ free(item->entry.mnt_opts);
+ free(item);
+ }
+}
- return !found_rw_fs;
+static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
+{
+ struct listnode* node;
+ list_for_each(node, rw_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
+ return item;
+ }
+ }
+ return NULL;
}
/* Remounting filesystems read-only is difficult when there are files
@@ -64,38 +129,92 @@ static int remount_ro_done(void)
* repeatedly until there are no more writable filesystems mounted on
* block devices.
*/
-static void remount_ro(void)
+static void remount_ro(void (*cb_on_remount)(const struct mntent*))
{
- int fd, cnt = 0;
+ int fd, cnt;
+ FILE* fp;
+ struct mntent* mentry;
+ struct listnode* node;
+
+ list_declare(rw_entries);
+ list_declare(ro_entries);
+
+ sync();
+ find_rw(&rw_entries);
/* Trigger the remount of the filesystems as read-only,
* which also marks them clean.
*/
- fd = open("/proc/sysrq-trigger", O_WRONLY);
+ fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
if (fd < 0) {
- return;
+ KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
+ /* TODO: Try to remount each rw parition manually in readonly mode.
+ * This may succeed if no process is using the partition.
+ */
+ goto out;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
+ close(fd);
+ KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
+ /* TODO: The same. Manually remount the paritions. */
+ goto out;
}
- write(fd, "u", 1);
close(fd);
-
/* Now poll /proc/mounts till it's done */
- while (!remount_ro_done() && (cnt < 50)) {
- usleep(100000);
+ cnt = 0;
+ while (cnt < READONLY_CHECK_TIMES) {
+ if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+ /* If we can't read /proc/mounts, just give up. */
+ KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+ goto out;
+ }
+ while ((mentry = getmntent(fp)) != NULL) {
+ if (!is_block_device(mentry->mnt_fsname) ||
+ !has_mount_option(mentry->mnt_opts, "ro")) {
+ continue;
+ }
+ mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
+ if (item) {
+ /* |item| has now been ro remounted. */
+ list_remove(&item->list);
+ list_add_tail(&ro_entries, &item->list);
+ }
+ }
+ endmntent(fp);
+ if (list_empty(&rw_entries)) {
+ /* All rw block devices are now readonly. */
+ break;
+ }
+ TEMP_FAILURE_RETRY(
+ usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
cnt++;
}
- return;
-}
+ list_for_each(node, &rw_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
+ item->entry.mnt_fsname);
+ }
+
+ if (cb_on_remount) {
+ list_for_each(node, &ro_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ cb_on_remount(&item->entry);
+ }
+ }
+out:
+ free_entries(&rw_entries);
+ free_entries(&ro_entries);
+}
-int android_reboot(int cmd, int flags UNUSED, const char *arg)
+int android_reboot_with_callback(
+ int cmd, int flags __unused, const char *arg,
+ void (*cb_on_remount)(const struct mntent*))
{
int ret;
-
- sync();
- remount_ro();
-
+ remount_ro(cb_on_remount);
switch (cmd) {
case ANDROID_RB_RESTART:
ret = reboot(RB_AUTOBOOT);
@@ -117,3 +236,7 @@ int android_reboot(int cmd, int flags UNUSED, const char *arg)
return ret;
}
+int android_reboot(int cmd, int flags, const char *arg)
+{
+ return android_reboot_with_callback(cmd, flags, arg, NULL);
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 3089a94..4a07d66 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -19,18 +19,119 @@
* ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
* used by the simulator.
*/
+#define LOG_TAG "ashmem"
-#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/ioctl.h>
-#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <linux/ashmem.h>
+
#include <cutils/ashmem.h>
+#include <log/log.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/* ashmem identity */
+static dev_t __ashmem_rdev;
+/*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler calls ashmem, we could get into a deadlock state.
+ */
+static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* logistics of getting file descriptor for ashmem */
+static int __ashmem_open_locked()
+{
+ int ret;
+ struct stat st;
+
+ int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+ if (fd < 0) {
+ return fd;
+ }
+
+ ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+ if (ret < 0) {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return ret;
+ }
+ if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
+ close(fd);
+ errno = ENOTTY;
+ return -1;
+ }
+
+ __ashmem_rdev = st.st_rdev;
+ return fd;
+}
-#define ASHMEM_DEVICE "/dev/ashmem"
+static int __ashmem_open()
+{
+ int fd;
+
+ pthread_mutex_lock(&__ashmem_lock);
+ fd = __ashmem_open_locked();
+ pthread_mutex_unlock(&__ashmem_lock);
+
+ return fd;
+}
+
+/* Make sure file descriptor references ashmem, negative number means false */
+static int __ashmem_is_ashmem(int fd)
+{
+ dev_t rdev;
+ struct stat st;
+
+ if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
+ return -1;
+ }
+
+ rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
+ if (S_ISCHR(st.st_mode) && st.st_rdev) {
+ pthread_mutex_lock(&__ashmem_lock);
+ rdev = __ashmem_rdev;
+ if (rdev) {
+ pthread_mutex_unlock(&__ashmem_lock);
+ } else {
+ int fd = __ashmem_open_locked();
+ if (fd < 0) {
+ pthread_mutex_unlock(&__ashmem_lock);
+ return -1;
+ }
+ rdev = __ashmem_rdev;
+ pthread_mutex_unlock(&__ashmem_lock);
+
+ close(fd);
+ }
+
+ if (st.st_rdev == rdev) {
+ return 0;
+ }
+ }
+
+ if (rdev) {
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
+ fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
+ major(rdev), minor(rdev));
+ } else {
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
+ fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
+ }
+ /* NOTREACHED */
+
+ errno = ENOTTY;
+ return -1;
+}
/*
* ashmem_create_region - creates a new ashmem region and returns the file
@@ -41,50 +142,77 @@
*/
int ashmem_create_region(const char *name, size_t size)
{
- int fd, ret;
+ int ret, save_errno;
- fd = open(ASHMEM_DEVICE, O_RDWR);
- if (fd < 0)
- return fd;
+ int fd = __ashmem_open();
+ if (fd < 0) {
+ return fd;
+ }
- if (name) {
- char buf[ASHMEM_NAME_LEN] = {0};
+ if (name) {
+ char buf[ASHMEM_NAME_LEN] = {0};
- strlcpy(buf, name, sizeof(buf));
- ret = ioctl(fd, ASHMEM_SET_NAME, buf);
- if (ret < 0)
- goto error;
- }
+ strlcpy(buf, name, sizeof(buf));
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
+ if (ret < 0) {
+ goto error;
+ }
+ }
- ret = ioctl(fd, ASHMEM_SET_SIZE, size);
- if (ret < 0)
- goto error;
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
+ if (ret < 0) {
+ goto error;
+ }
- return fd;
+ return fd;
error:
- close(fd);
- return ret;
+ save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return ret;
}
int ashmem_set_prot_region(int fd, int prot)
{
- return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+ int ret = __ashmem_is_ashmem(fd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
}
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_PIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+
+ int ret = __ashmem_is_ashmem(fd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_UNPIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+
+ int ret = __ashmem_is_ashmem(fd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
}
int ashmem_get_size_region(int fd)
{
- return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+ int ret = __ashmem_is_ashmem(fd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index abc4f94..99a2759 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -73,8 +73,11 @@ int ashmem_get_size_region(int fd)
return -1;
}
- // Check if this is an "ashmem" region.
- // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+ /*
+ * Check if this is an "ashmem" region.
+ * TODO: This is very hacky, and can easily break.
+ * We need some reliable indicator.
+ */
if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
errno = ENOTTY;
return -1;
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 70dc8c4..b302bef 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -53,8 +53,12 @@ static inline SchedPolicy _policy(SchedPolicy p)
#define TIMER_SLACK_FG 50000
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
+static pthread_once_t sched_once = PTHREAD_ONCE_INIT;
+static pthread_once_t cpuset_once = PTHREAD_ONCE_INIT;
static int __sys_supports_schedgroups = -1;
+static int __sys_supports_cpusets = -1;
+static char proc_name[32] = {0};
// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
static int bg_cgroup_fd = -1;
@@ -65,15 +69,8 @@ static int system_bg_cpuset_fd = -1;
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
-/* Add tid to the scheduling group defined by the policy */
-static int add_tid_to_cgroup(int tid, int fd)
+static int write_tid_to_fd(int tid, int fd)
{
- if (fd < 0) {
- SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
- errno = EINVAL;
- return -1;
- }
-
// specialized itoa -- works for tid > 0
char text[22];
char *end = text + sizeof(text) - 1;
@@ -91,8 +88,42 @@ static int add_tid_to_cgroup(int tid, int fd)
*/
if (errno == ESRCH)
return 0;
- SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
- ptr, strerror(errno), fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Add tid to the scheduling group defined by the policy */
+static int add_tid_to_cgroup(int tid, int fd)
+{
+ if (fd < 0) {
+ SLOGE("%s add_tid_to_cgroup failed; fd=%d\n", proc_name, fd);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (write_tid_to_fd(tid, fd) != 0) {
+ SLOGW("%s add_tid_to_cgroup failed to write '%d' (%s); fd=%d\n",
+ proc_name, tid, strerror(errno), fd);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_tid_to_cpuset(int tid, int fd)
+{
+ if (fd < 0) {
+ SLOGE("%s add_tid_to_cpuset failed; fd=%d\n", proc_name, fd);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (write_tid_to_fd(tid, fd) != 0) {
+ SLOGW("%s add_tid_to_cpuset failed to write '%d' (%s); fd=%d\n",
+ proc_name, tid, strerror(errno), fd);
errno = EINVAL;
return -1;
}
@@ -101,34 +132,95 @@ static int add_tid_to_cgroup(int tid, int fd)
}
static void __initialize(void) {
+ int pfd;
+ int ptid = gettid();
+
+ sprintf(proc_name, "/proc/%d/cmdline", ptid);
+
+ pfd = open(proc_name, O_RDONLY);
+ memset(proc_name, 0, sizeof(proc_name));
+ if (pfd > 0) {
+ read(pfd, proc_name, sizeof(proc_name) - 1);
+ close(pfd);
+ }
+}
+
+static void __init_sched(void) {
char* filename;
+
+ pthread_once(&the_once, __initialize);
+
if (!access("/dev/cpuctl/tasks", F_OK)) {
__sys_supports_schedgroups = 1;
filename = "/dev/cpuctl/tasks";
fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
if (fg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ SLOGE("%s open of %s failed: %s\n", proc_name, filename, strerror(errno));
+ __sys_supports_schedgroups = 0;
}
filename = "/dev/cpuctl/bg_non_interactive/tasks";
bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
if (bg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ SLOGE("%s open of %s failed: %s\n", proc_name, filename, strerror(errno));
+ __sys_supports_schedgroups = 0;
+ }
+
+ if (!__sys_supports_schedgroups) {
+ close(bg_cgroup_fd);
+ bg_cgroup_fd = -1;
+
+ close(fg_cgroup_fd);
+ fg_cgroup_fd = -1;
}
} else {
__sys_supports_schedgroups = 0;
}
+}
+
+static void __init_cpuset(void) {
+ char *filename;
+
+ pthread_once(&the_once, __initialize);
#ifdef USE_CPUSETS
if (!access("/dev/cpuset/tasks", F_OK)) {
+ __sys_supports_cpusets = 1;
filename = "/dev/cpuset/foreground/tasks";
fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ if (fg_cpuset_fd < 0) {
+ SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno));
+ __sys_supports_cpusets = 0;
+ }
+
filename = "/dev/cpuset/background/tasks";
bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ if (bg_cpuset_fd < 0) {
+ SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno));
+ __sys_supports_cpusets = 0;
+ }
+
filename = "/dev/cpuset/system-background/tasks";
system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ if (system_bg_cpuset_fd < 0) {
+ SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno));
+ __sys_supports_cpusets = 0;
+ }
+
+ if (!__sys_supports_cpusets) {
+ close(fg_cpuset_fd);
+ fg_cpuset_fd = -1;
+
+ close(bg_cpuset_fd);
+ bg_cpuset_fd = -1;
+
+ close(system_bg_cpuset_fd);
+ system_bg_cpuset_fd = -1;
+ }
+ } else {
+ __sys_supports_cpusets = 0;
}
#endif
@@ -193,11 +285,11 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
return 0;
}
- SLOGE("Failed to find cpu subsys");
+ SLOGE("%s Failed to find cpu subsys", proc_name);
fclose(fp);
return -1;
out_bad_data:
- SLOGE("Bad cgroup data {%s}", lineBuf);
+ SLOGE("%s Bad cgroup data {%s}", proc_name, lineBuf);
fclose(fp);
return -1;
#else
@@ -211,7 +303,8 @@ int get_sched_policy(int tid, SchedPolicy *policy)
if (tid == 0) {
tid = gettid();
}
- pthread_once(&the_once, __initialize);
+
+ pthread_once(&sched_once, __init_sched);
if (__sys_supports_schedgroups) {
char grpBuf[32];
@@ -250,8 +343,13 @@ int set_cpuset_policy(int tid, SchedPolicy policy)
if (tid == 0) {
tid = gettid();
}
+
+ pthread_once(&cpuset_once, __init_cpuset);
+
+ if (!__sys_supports_cpusets)
+ return set_sched_policy(tid, policy);
+
policy = _policy(policy);
- pthread_once(&the_once, __initialize);
int fd;
switch (policy) {
@@ -271,7 +369,7 @@ int set_cpuset_policy(int tid, SchedPolicy policy)
break;
}
- if (add_tid_to_cgroup(tid, fd) != 0) {
+ if (add_tid_to_cpuset(tid, fd) != 0) {
if (errno != ESRCH && errno != ENOENT)
return -errno;
}
@@ -286,7 +384,8 @@ int set_sched_policy(int tid, SchedPolicy policy)
tid = gettid();
}
policy = _policy(policy);
- pthread_once(&the_once, __initialize);
+
+ pthread_once(&sched_once, __init_sched);
#if POLICY_DEBUG
char statfile[64];