diff options
Diffstat (limited to 'libcutils')
-rw-r--r-- | libcutils/android_reboot.c | 187 | ||||
-rw-r--r-- | libcutils/ashmem-dev.c | 184 | ||||
-rw-r--r-- | libcutils/ashmem-host.c | 7 | ||||
-rw-r--r-- | libcutils/sched_policy.c | 135 |
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]; |