Snap for 8730993 from 54e25eb8776396e4b548c57702cf3d807624ba00 to mainline-tzdata3-release
Change-Id: I3bf4325cbc5c1d2fb6908b00ab1c1b30602c334b
diff --git a/Android.bp b/Android.bp
index 0481c96..4f53ded 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,11 +12,7 @@
cc_binary {
name: "lmkd",
- srcs: [
- "lmkd.cpp",
- "reaper.cpp",
- "watchdog.cpp",
- ],
+ srcs: ["lmkd.cpp"],
shared_libs: [
"libcutils",
"liblog",
@@ -40,7 +36,6 @@
init_rc: ["lmkd.rc"],
defaults: ["stats_defaults"],
logtags: ["event.logtags"],
- afdo: true,
}
cc_library_static {
@@ -55,7 +50,6 @@
defaults: ["stats_defaults"],
shared_libs: [
"liblog",
- "libprocessgroup",
],
}
@@ -65,7 +59,6 @@
recovery_available: true,
shared_libs: [
"libcutils",
- "libprocessgroup",
],
export_include_dirs: ["include"],
cppflags: [
diff --git a/event.logtags b/event.logtags
index dcab73c..5382b49 100644
--- a/event.logtags
+++ b/event.logtags
@@ -21,7 +21,6 @@
# 2: int64_t
# 3: string
# 4: list
-# 5: float
#
# The data unit is a number taken from the following list:
# 1: Number of objects
@@ -36,4 +35,4 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
# for killinfo logs
-10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1),(MsSinceEvent|1),(MsSincePrevWakeup|1),(WakeupsSinceEvent|1),(SkippedWakeups|1),(TaskSwapSize|1),(GPU|1),(Thrashing|1),(MaxThrashing|1),(PsiMemSome|5),(PsiMemFull|5),(PsiIoSome|5),(PsiIoFull|5),(PsiCpuSome|5)
+10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1),(MsSinceEvent|1),(MsSincePrevWakeup|1),(WakeupsSinceEvent|1),(SkippedWakeups|1),(TaskSwapSize|1),(GPU|1)
diff --git a/liblmkd_utils.cpp b/liblmkd_utils.cpp
index e5e99de..55d7f62 100644
--- a/liblmkd_utils.cpp
+++ b/liblmkd_utils.cpp
@@ -22,9 +22,8 @@
#include <stdio.h>
#include <unistd.h>
-#include <cutils/sockets.h>
#include <liblmkd_utils.h>
-#include <processgroup/processgroup.h>
+#include <cutils/sockets.h>
int lmkd_connect() {
return socket_local_client("lmkd",
@@ -79,6 +78,34 @@
}
int create_memcg(uid_t uid, pid_t pid) {
- return createProcessGroup(uid, pid, true) == 0 ? 0 : -1;
+ char buf[256];
+ int tasks_file;
+ int written;
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+ tasks_file = open(buf, O_WRONLY);
+ if (tasks_file < 0) {
+ return -2;
+ }
+ written = snprintf(buf, sizeof(buf), "%u", pid);
+ if (__predict_false(written >= (int)sizeof(buf))) {
+ written = sizeof(buf) - 1;
+ }
+ written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+ close(tasks_file);
+
+ return (written < 0) ? -3 : 0;
}
diff --git a/libpsi/include/psi/psi.h b/libpsi/include/psi/psi.h
index 9162446..cd49e8b 100644
--- a/libpsi/include/psi/psi.h
+++ b/libpsi/include/psi/psi.h
@@ -22,29 +22,12 @@
__BEGIN_DECLS
-#define PSI_PATH_MEMORY "/proc/pressure/memory"
-#define PSI_PATH_IO "/proc/pressure/io"
-#define PSI_PATH_CPU "/proc/pressure/cpu"
-
enum psi_stall_type {
PSI_SOME,
PSI_FULL,
PSI_TYPE_COUNT
};
-struct psi_stats {
- float avg10;
- float avg60;
- float avg300;
- unsigned long total;
-};
-
-struct psi_data {
- struct psi_stats mem_stats[PSI_TYPE_COUNT];
- struct psi_stats io_stats[PSI_TYPE_COUNT];
- struct psi_stats cpu_stats[PSI_TYPE_COUNT];
-};
-
/*
* Initializes psi monitor.
* stall_type, threshold_us and window_us are monitor parameters
@@ -80,13 +63,6 @@
*/
void destroy_psi_monitor(int fd);
-/*
- * Parse psi file line content. Expected file format is:
- * some avg10=0.00 avg60=0.00 avg300=0.00 total=0
- * full avg10=0.00 avg60=0.00 avg300=0.00 total=0
- */
-int parse_psi_line(char *line, enum psi_stall_type stall_type, struct psi_stats stats[]);
-
__END_DECLS
#endif // __ANDROID_PSI_H__
diff --git a/libpsi/psi.cpp b/libpsi/psi.cpp
index 54f9971..89f07ed 100644
--- a/libpsi/psi.cpp
+++ b/libpsi/psi.cpp
@@ -28,6 +28,8 @@
#include <stdio.h>
#include "psi/psi.h"
+#define PSI_MON_FILE_MEMORY "/proc/pressure/memory"
+
static const char* stall_type_name[] = {
"some",
"full",
@@ -39,7 +41,7 @@
int res;
char buf[256];
- fd = TEMP_FAILURE_RETRY(open(PSI_PATH_MEMORY, O_WRONLY | O_CLOEXEC));
+ fd = TEMP_FAILURE_RETRY(open(PSI_MON_FILE_MEMORY, O_WRONLY | O_CLOEXEC));
if (fd < 0) {
ALOGE("No kernel psi monitor support (errno=%d)", errno);
return -1;
@@ -59,7 +61,7 @@
if (res >= (ssize_t)sizeof(buf)) {
ALOGE("%s line overflow for psi stall type '%s'",
- PSI_PATH_MEMORY, stall_type_name[stall_type]);
+ PSI_MON_FILE_MEMORY, stall_type_name[stall_type]);
errno = EINVAL;
goto err;
}
@@ -67,7 +69,7 @@
res = TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf) + 1));
if (res < 0) {
ALOGE("%s write failed for psi stall type '%s'; errno=%d",
- PSI_PATH_MEMORY, stall_type_name[stall_type], errno);
+ PSI_MON_FILE_MEMORY, stall_type_name[stall_type], errno);
goto err;
}
@@ -100,17 +102,3 @@
close(fd);
}
}
-
-int parse_psi_line(char *line, enum psi_stall_type stall_type, struct psi_stats stats[]) {
- char type_name[5];
- struct psi_stats *stat = &stats[stall_type];
-
- if (!line || sscanf(line, "%4s avg10=%f avg60=%f avg300=%f total=%lu",
- type_name, &stat->avg10, &stat->avg60, &stat->avg300, &stat->total) != 5) {
- return -1;
- }
- if (strcmp(type_name, stall_type_name[stall_type])) {
- return -1;
- }
- return 0;
-}
diff --git a/lmkd.cpp b/lmkd.cpp
index 741e891..68f9729 100644
--- a/lmkd.cpp
+++ b/lmkd.cpp
@@ -16,10 +16,12 @@
#define LOG_TAG "lowmemorykiller"
+#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <pwd.h>
#include <sched.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -28,17 +30,17 @@
#include <sys/eventfd.h>
#include <sys/mman.h>
#include <sys/pidfd.h>
+#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
-#include <algorithm>
-#include <array>
-#include <shared_mutex>
-
#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <liblmkd_utils.h>
#include <lmkd.h>
@@ -46,12 +48,10 @@
#include <log/log_event_list.h>
#include <log/log_time.h>
#include <private/android_filesystem_config.h>
-#include <processgroup/processgroup.h>
#include <psi/psi.h>
+#include <system/thread_defs.h>
-#include "reaper.h"
#include "statslog.h"
-#include "watchdog.h"
#define BPF_FD_JUST_USE_INT
#include "BpfSyscallWrappers.h"
@@ -65,20 +65,13 @@
#define ATRACE_TAG ATRACE_TAG_ALWAYS
#include <cutils/trace.h>
-static inline void trace_kill_start(int pid, const char *desc) {
- ATRACE_INT("kill_one_process", pid);
- ATRACE_BEGIN(desc);
-}
-
-static inline void trace_kill_end() {
- ATRACE_END();
- ATRACE_INT("kill_one_process", 0);
-}
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END() ATRACE_INT(__FUNCTION__, 0);
#else /* LMKD_TRACE_KILLS */
-static inline void trace_kill_start(int, const char *) {}
-static inline void trace_kill_end() {}
+#define TRACE_KILL_START(pid) ((void)(pid))
+#define TRACE_KILL_END() ((void)0)
#endif /* LMKD_TRACE_KILLS */
@@ -86,6 +79,9 @@
#define __unused __attribute__((__unused__))
#endif
+#define MEMCG_SYSFS_PATH "/dev/memcg/"
+#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
+#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define MEMINFO_PATH "/proc/meminfo"
#define VMSTAT_PATH "/proc/vmstat"
@@ -103,6 +99,7 @@
#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define EIGHT_MEGA (1 << 23)
#define TARGET_UPDATE_MIN_INTERVAL_MS 1000
@@ -139,6 +136,9 @@
/* Polling period after PSI signal when pressure is low */
#define PSI_POLL_PERIOD_LONG_MS 100
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
#define FAIL_REPORT_RLIMIT_MS 1000
/*
@@ -160,8 +160,6 @@
#define LMKD_REINIT_PROP "lmkd.reinit"
-#define WATCHDOG_TIMEOUT_SEC 2
-
/* default to old in-kernel interface if no memory pressure events */
static bool use_inkernel_interface = true;
static bool has_inkernel_module;
@@ -214,7 +212,6 @@
static int thrashing_critical_pct;
static int swap_util_max;
static int64_t filecache_min_kb;
-static int64_t stall_limit_critical;
static bool use_psi_monitors = false;
static int kpoll_fd;
static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
@@ -224,8 +221,6 @@
};
static android_log_context ctx;
-static Reaper reaper;
-static int reaper_comm_fd[2];
enum polling_update {
POLLING_DO_NOT_CHANGE,
@@ -275,9 +270,9 @@
/*
* 1 ctrl listen socket, 3 ctrl data socket, 3 memory pressure levels,
- * 1 lmk events + 1 fd to wait for process death + 1 fd to receive kill failure notifications
+ * 1 lmk events + 1 fd to wait for process death
*/
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT + 1 + 1 + 1)
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT + 1 + 1)
static int epollfd;
static int maxevents;
@@ -285,8 +280,8 @@
#define OOM_SCORE_ADJ_MIN (-1000)
#define OOM_SCORE_ADJ_MAX 1000
-static std::array<int, MAX_TARGETS> lowmem_adj;
-static std::array<int, MAX_TARGETS> lowmem_minfree;
+static int lowmem_adj[MAX_TARGETS];
+static int lowmem_minfree[MAX_TARGETS];
static int lowmem_targets_size;
/* Fields to parse in /proc/zoneinfo */
@@ -525,11 +520,6 @@
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
-
-// protects procadjslot_list from concurrent access
-static std::shared_mutex adjslot_list_lock;
-// procadjslot_list should be modified only from the main thread while exclusively holding
-// adjslot_list_lock. Readers from non-main threads should hold adjslot_list_lock shared lock.
static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
#define MAX_DISTINCT_OOM_ADJ 32
@@ -552,7 +542,7 @@
static void destroy_monitors();
static int clamp(int low, int high, int value) {
- return std::max(std::min(value, high), low);
+ return max(min(value, high), low);
}
static bool parse_int64(const char* str, int64_t* ret) {
@@ -833,7 +823,7 @@
while (1) {
char rd_buf[256];
- int bytes_read = TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf) - 1, 0));
+ int bytes_read = TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
if (bytes_read <= 0) break;
rd_buf[bytes_read] = '\0';
@@ -919,18 +909,13 @@
return asl == head ? NULL : asl;
}
-// Should be modified only from the main thread.
static void proc_slot(struct proc *procp) {
int adjslot = ADJTOSLOT(procp->oomadj);
- std::scoped_lock lock(adjslot_list_lock);
adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
}
-// Should be modified only from the main thread.
static void proc_unslot(struct proc *procp) {
- std::scoped_lock lock(adjslot_list_lock);
-
adjslot_remove(&procp->asl);
}
@@ -1180,12 +1165,9 @@
soft_limit_mult = 64;
}
- std::string path;
- if (!CgroupGetAttributePathForTask("MemSoftLimit", params.pid, &path)) {
- ALOGE("Querying MemSoftLimit path failed");
- return;
- }
-
+ snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
+ "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+ params.uid, params.pid);
snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
/*
@@ -1195,7 +1177,7 @@
is_system_server = (params.oomadj == SYSTEM_ADJ &&
(pwdrec = getpwnam("system")) != NULL &&
params.uid == pwdrec->pw_uid);
- writefilestring(path.c_str(), val, !is_system_server);
+ writefilestring(path, val, !is_system_server);
}
procp = pid_lookup(params.pid);
@@ -1377,9 +1359,8 @@
static struct timespec last_req_tm;
struct timespec curr_tm;
- if (ntargets < 1 || ntargets > (int)lowmem_adj.size()) {
+ if (ntargets < 1 || ntargets > (int)ARRAY_SIZE(lowmem_adj))
return;
- }
/*
* Ratelimit minfree updates to once per TARGET_UPDATE_MIN_INTERVAL_MS
@@ -1471,9 +1452,8 @@
switch(cmd) {
case LMK_TARGET:
targets = nargs / 2;
- if (nargs & 0x1 || targets > (int)lowmem_adj.size()) {
+ if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
- }
cmd_target(targets, packet);
break;
case LMK_PROCPRIO:
@@ -1917,53 +1897,6 @@
return 0;
}
-static int psi_parse(struct reread_data *file_data, struct psi_stats stats[], bool full) {
- char *buf;
- char *save_ptr;
- char *line;
-
- if ((buf = reread_file(file_data)) == NULL) {
- return -1;
- }
-
- line = strtok_r(buf, "\n", &save_ptr);
- if (parse_psi_line(line, PSI_SOME, stats)) {
- return -1;
- }
- if (full) {
- line = strtok_r(NULL, "\n", &save_ptr);
- if (parse_psi_line(line, PSI_FULL, stats)) {
- return -1;
- }
- }
-
- return 0;
-}
-
-static int psi_parse_mem(struct psi_data *psi_data) {
- static struct reread_data file_data = {
- .filename = PSI_PATH_MEMORY,
- .fd = -1,
- };
- return psi_parse(&file_data, psi_data->mem_stats, true);
-}
-
-static int psi_parse_io(struct psi_data *psi_data) {
- static struct reread_data file_data = {
- .filename = PSI_PATH_IO,
- .fd = -1,
- };
- return psi_parse(&file_data, psi_data->io_stats, true);
-}
-
-static int psi_parse_cpu(struct psi_data *psi_data) {
- static struct reread_data file_data = {
- .filename = PSI_PATH_CPU,
- .fd = -1,
- };
- return psi_parse(&file_data, psi_data->cpu_stats, false);
-}
-
enum wakeup_reason {
Event,
Polling
@@ -1999,96 +1932,38 @@
}
}
-struct kill_info {
- enum kill_reasons kill_reason;
- const char *kill_desc;
- int thrashing;
- int max_thrashing;
-};
-
static void killinfo_log(struct proc* procp, int min_oom_score, int rss_kb,
- int swap_kb, struct kill_info *ki, union meminfo *mi,
- struct wakeup_info *wi, struct timespec *tm, struct psi_data *pd) {
+ int swap_kb, int kill_reason, union meminfo *mi,
+ struct wakeup_info *wi, struct timespec *tm) {
/* log process information */
android_log_write_int32(ctx, procp->pid);
android_log_write_int32(ctx, procp->uid);
android_log_write_int32(ctx, procp->oomadj);
android_log_write_int32(ctx, min_oom_score);
- android_log_write_int32(ctx, std::min(rss_kb, (int)INT32_MAX));
- android_log_write_int32(ctx, ki ? ki->kill_reason : NONE);
+ android_log_write_int32(ctx, (int32_t)min(rss_kb, INT32_MAX));
+ android_log_write_int32(ctx, kill_reason);
/* log meminfo fields */
for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
- android_log_write_int32(ctx,
- mi ? std::min(mi->arr[field_idx] * page_k, (int64_t)INT32_MAX) : 0);
+ android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
}
/* log lmkd wakeup information */
- if (wi) {
- android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->last_event_tm, tm));
- android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->prev_wakeup_tm, tm));
- android_log_write_int32(ctx, wi->wakeups_since_event);
- android_log_write_int32(ctx, wi->skipped_wakeups);
- } else {
- android_log_write_int32(ctx, 0);
- android_log_write_int32(ctx, 0);
- android_log_write_int32(ctx, 0);
- android_log_write_int32(ctx, 0);
- }
-
- android_log_write_int32(ctx, std::min(swap_kb, (int)INT32_MAX));
- android_log_write_int32(ctx, mi ? (int32_t)mi->field.total_gpu_kb : 0);
- if (ki) {
- android_log_write_int32(ctx, ki->thrashing);
- android_log_write_int32(ctx, ki->max_thrashing);
- } else {
- android_log_write_int32(ctx, 0);
- android_log_write_int32(ctx, 0);
- }
-
- if (pd) {
- android_log_write_float32(ctx, pd->mem_stats[PSI_SOME].avg10);
- android_log_write_float32(ctx, pd->mem_stats[PSI_FULL].avg10);
- android_log_write_float32(ctx, pd->io_stats[PSI_SOME].avg10);
- android_log_write_float32(ctx, pd->io_stats[PSI_FULL].avg10);
- android_log_write_float32(ctx, pd->cpu_stats[PSI_SOME].avg10);
- } else {
- for (int i = 0; i < 5; i++) {
- android_log_write_float32(ctx, 0);
- }
- }
+ android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->last_event_tm, tm));
+ android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->prev_wakeup_tm, tm));
+ android_log_write_int32(ctx, wi->wakeups_since_event);
+ android_log_write_int32(ctx, wi->skipped_wakeups);
+ android_log_write_int32(ctx, (int32_t)min(swap_kb, INT32_MAX));
+ android_log_write_int32(ctx, (int32_t)mi->field.total_gpu_kb);
android_log_write_list(ctx, LOG_ID_EVENTS);
android_log_reset(ctx);
}
-// Note: returned entry is only an anchor and does not hold a valid process info.
-// When called from a non-main thread, adjslot_list_lock read lock should be taken.
-static struct proc *proc_adj_head(int oomadj) {
- return (struct proc *)&procadjslot_list[ADJTOSLOT(oomadj)];
-}
-
-// When called from a non-main thread, adjslot_list_lock read lock should be taken.
-static struct proc *proc_adj_tail(int oomadj) {
+static struct proc *proc_adj_lru(int oomadj) {
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
-// When called from a non-main thread, adjslot_list_lock read lock should be taken.
-static struct proc *proc_adj_prev(int oomadj, int pid) {
- struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
- struct adjslot_list *curr = adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
-
- while (curr != head) {
- if (((struct proc *)curr)->pid == pid) {
- return (struct proc *)curr->prev;
- }
- curr = curr->prev;
- }
-
- return NULL;
-}
-
-// When called from a non-main thread, adjslot_list_lock read lock should be taken.
static struct proc *proc_get_heaviest(int oomadj) {
struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
struct adjslot_list *curr = head->next;
@@ -2112,54 +1987,40 @@
return maxprocp;
}
-static bool find_victim(int oom_score, int prev_pid, struct proc &target_proc) {
- struct proc *procp;
- std::shared_lock lock(adjslot_list_lock);
+static void set_process_group_and_prio(int pid, SchedPolicy sp, int prio) {
+ DIR* d;
+ char proc_path[PATH_MAX];
+ struct dirent* de;
- if (!prev_pid) {
- procp = proc_adj_tail(oom_score);
- } else {
- procp = proc_adj_prev(oom_score, prev_pid);
- if (!procp) {
- // pid was removed, restart at the tail
- procp = proc_adj_tail(oom_score);
- }
+ snprintf(proc_path, sizeof(proc_path), "/proc/%d/task", pid);
+ if (!(d = opendir(proc_path))) {
+ ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno,
+ pid);
+ return;
}
- // the list is empty at this oom_score or we looped through it
- if (!procp || procp == proc_adj_head(oom_score)) {
- return false;
- }
+ while ((de = readdir(d))) {
+ int t_pid;
- // make a copy because original might be destroyed after adjslot_list_lock is released
- target_proc = *procp;
+ if (de->d_name[0] == '.') continue;
+ t_pid = atoi(de->d_name);
- return true;
-}
-
-static void watchdog_callback() {
- int prev_pid = 0;
-
- ALOGW("lmkd watchdog timed out!");
- for (int oom_score = OOM_SCORE_ADJ_MAX; oom_score >= 0;) {
- struct proc target;
-
- if (!find_victim(oom_score, prev_pid, target)) {
- oom_score--;
- prev_pid = 0;
+ if (!t_pid) {
+ ALOGW("Failed to get t_pid for '%s' of pid(%d)", de->d_name, pid);
continue;
}
- if (reaper.kill({ target.pidfd, target.pid, target.uid }, true) == 0) {
- ALOGW("lmkd watchdog killed process %d, oom_score_adj %d", target.pid, oom_score);
- killinfo_log(&target, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
- break;
+ if (setpriority(PRIO_PROCESS, t_pid, prio) && errno != ESRCH) {
+ ALOGW("Unable to raise priority of killing t_pid (%d): errno=%d", t_pid, errno);
}
- prev_pid = target.pid;
- }
-}
-static Watchdog watchdog(WATCHDOG_TIMEOUT_SEC, watchdog_callback);
+ if (set_cpuset_policy(t_pid, sp)) {
+ ALOGW("Failed to set_cpuset_policy on pid(%d) t_pid(%d) to %d", pid, t_pid, (int)sp);
+ continue;
+ }
+ }
+ closedir(d);
+}
static bool is_kill_pending(void) {
char buf[24];
@@ -2231,19 +2092,6 @@
poll_params->update = POLLING_RESUME;
}
-static void kill_fail_handler(int data __unused, uint32_t events __unused,
- struct polling_params *poll_params) {
- int pid;
-
- // Extract pid from the communication pipe. Clearing the pipe this way allows further
- // epoll_wait calls to sleep until the next event.
- if (TEMP_FAILURE_RETRY(read(reaper_comm_fd[0], &pid, sizeof(pid))) != sizeof(pid)) {
- ALOGE("thread communication read failed: %s", strerror(errno));
- }
- stop_wait_for_proc_kill(false);
- poll_params->update = POLLING_RESUME;
-}
-
static void start_wait_for_proc_kill(int pid_or_fd) {
static struct event_handler_info kill_done_hinfo = { 0, kill_done_handler };
struct epoll_event epev;
@@ -2272,15 +2120,21 @@
maxevents++;
}
+struct kill_info {
+ enum kill_reasons kill_reason;
+ const char *kill_desc;
+ int thrashing;
+ int max_thrashing;
+};
+
/* Kill one process specified by procp. Returns the size (in pages) of the process killed */
static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_info *ki,
- union meminfo *mi, struct wakeup_info *wi, struct timespec *tm,
- struct psi_data *pd) {
+ union meminfo *mi, struct wakeup_info *wi, struct timespec *tm) {
int pid = procp->pid;
int pidfd = procp->pidfd;
uid_t uid = procp->uid;
char *taskname;
- int kill_result;
+ int r;
int result = -1;
struct memory_stat *mem_st;
struct kill_stat kill_st;
@@ -2288,7 +2142,6 @@
int64_t rss_kb;
int64_t swap_kb;
char buf[PAGE_SIZE];
- char desc[LINE_MAX];
if (!read_proc_status(pid, buf, sizeof(buf))) {
goto out;
@@ -2317,23 +2170,28 @@
mem_st = stats_read_memory_stat(per_app_memcg, pid, uid, rss_kb * 1024, swap_kb * 1024);
- snprintf(desc, sizeof(desc), "lmk,%d,%d,%d,%d,%d", pid, ki ? (int)ki->kill_reason : -1,
- procp->oomadj, min_oom_score, ki ? ki->max_thrashing : -1);
+ TRACE_KILL_START(pid);
- trace_kill_start(pid, desc);
+ /* CAP_KILL required */
+ if (pidfd < 0) {
+ start_wait_for_proc_kill(pid);
+ r = kill(pid, SIGKILL);
+ } else {
+ start_wait_for_proc_kill(pidfd);
+ r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
+ }
- start_wait_for_proc_kill(pidfd < 0 ? pid : pidfd);
- kill_result = reaper.kill({ pidfd, pid, uid }, false);
+ TRACE_KILL_END();
- trace_kill_end();
-
- if (kill_result) {
+ if (r) {
stop_wait_for_proc_kill(false);
ALOGE("kill(%d): errno=%d", pid, errno);
/* Delete process record even when we fail to kill so that we don't get stuck on it */
goto out;
}
+ set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+
last_kill_tm = *tm;
inc_killcnt(procp->oomadj);
@@ -2342,6 +2200,7 @@
kill_st.kill_reason = ki->kill_reason;
kill_st.thrashing = ki->thrashing;
kill_st.max_thrashing = ki->max_thrashing;
+ killinfo_log(procp, min_oom_score, rss_kb, swap_kb, ki->kill_reason, mi, wi, tm);
ALOGI("Kill '%s' (%d), uid %d, oom_score_adj %d to free %" PRId64 "kB rss, %" PRId64
"kB swap; reason: %s", taskname, pid, uid, procp->oomadj, rss_kb, swap_kb,
ki->kill_desc);
@@ -2349,10 +2208,10 @@
kill_st.kill_reason = NONE;
kill_st.thrashing = 0;
kill_st.max_thrashing = 0;
+ killinfo_log(procp, min_oom_score, rss_kb, swap_kb, NONE, mi, wi, tm);
ALOGI("Kill '%s' (%d), uid %d, oom_score_adj %d to free %" PRId64 "kB rss, %" PRId64
"kb swap", taskname, pid, uid, procp->oomadj, rss_kb, swap_kb);
}
- killinfo_log(procp, min_oom_score, rss_kb, swap_kb, ki, mi, wi, tm, pd);
kill_st.uid = static_cast<int32_t>(uid);
kill_st.taskname = taskname;
@@ -2380,8 +2239,7 @@
* Returns size of the killed process.
*/
static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union meminfo *mi,
- struct wakeup_info *wi, struct timespec *tm,
- struct psi_data *pd) {
+ struct wakeup_info *wi, struct timespec *tm) {
int i;
int killed_size = 0;
bool lmk_state_change_start = false;
@@ -2400,12 +2258,12 @@
while (true) {
procp = choose_heaviest_task ?
- proc_get_heaviest(i) : proc_adj_tail(i);
+ proc_get_heaviest(i) : proc_adj_lru(i);
if (!procp)
break;
- killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm, pd);
+ killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm);
if (killed_size >= 0) {
if (!lmk_state_change_start) {
lmk_state_change_start = true;
@@ -2552,6 +2410,7 @@
static int64_t base_file_lru;
static int64_t init_pgscan_kswapd;
static int64_t init_pgscan_direct;
+ static int64_t swap_low_threshold;
static bool killing;
static int thrashing_limit = thrashing_limit_pct;
static struct zone_watermarks watermarks;
@@ -2564,7 +2423,6 @@
union meminfo mi;
union vmstat vs;
- struct psi_data psi_data;
struct timespec curr_tm;
int64_t thrashing = 0;
bool swap_is_low = false;
@@ -2577,10 +2435,8 @@
bool cut_thrashing_limit = false;
int min_score_adj = 0;
int swap_util = 0;
- int64_t swap_low_threshold;
long since_thrashing_reset_ms;
int64_t workingset_refault_file;
- bool critical_stall = false;
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
ALOGE("Failed to get current time");
@@ -2627,10 +2483,10 @@
/* Check free swap levels */
if (swap_free_low_percentage) {
- swap_low_threshold = mi.field.total_swap * swap_free_low_percentage / 100;
+ if (!swap_low_threshold) {
+ swap_low_threshold = mi.field.total_swap * swap_free_low_percentage / 100;
+ }
swap_is_low = mi.field.free_swap < swap_low_threshold;
- } else {
- swap_low_threshold = 0;
}
/* Identify reclaim state */
@@ -2713,9 +2569,6 @@
/* Find out which watermark is breached if any */
wmark = get_lowest_watermark(&mi, &watermarks);
- if (!psi_parse_mem(&psi_data)) {
- critical_stall = psi_data.mem_stats[PSI_FULL].avg10 > (float)stall_limit_critical;
- }
/*
* TODO: move this logic into a separate function
* Decide if killing a process is necessary and record the reason
@@ -2813,14 +2666,7 @@
.thrashing = (int)thrashing,
.max_thrashing = max_thrashing,
};
-
- /* Allow killing perceptible apps if the system is stalled */
- if (critical_stall) {
- min_score_adj = 0;
- }
- psi_parse_io(&psi_data);
- psi_parse_cpu(&psi_data);
- int pages_freed = find_and_kill_process(min_score_adj, &ki, &mi, &wi, &curr_tm, &psi_data);
+ int pages_freed = find_and_kill_process(min_score_adj, &ki, &mi, &wi, &curr_tm);
if (pages_freed > 0) {
killing = true;
max_thrashing = 0;
@@ -2862,16 +2708,6 @@
}
}
-static std::string GetCgroupAttributePath(const char* attr) {
- std::string path;
- if (!CgroupGetAttributePath(attr, &path)) {
- ALOGE("Unknown cgroup attribute %s", attr);
- }
- return path;
-}
-
-// The implementation of this function relies on memcg statistics that are only available in the
-// v1 cgroup hierarchy.
static void mp_event_common(int data, uint32_t events, struct polling_params *poll_params) {
unsigned long long evcount;
int64_t mem_usage, memsw_usage;
@@ -2884,14 +2720,12 @@
long other_free = 0, other_file = 0;
int min_score_adj;
int minfree = 0;
- static const std::string mem_usage_path = GetCgroupAttributePath("MemUsage");
static struct reread_data mem_usage_file_data = {
- .filename = mem_usage_path.c_str(),
+ .filename = MEMCG_MEMORY_USAGE,
.fd = -1,
};
- static const std::string memsw_usage_path = GetCgroupAttributePath("MemAndSwapUsage");
static struct reread_data memsw_usage_file_data = {
- .filename = memsw_usage_path.c_str(),
+ .filename = MEMCG_MEMORYSW_USAGE,
.fd = -1,
};
static struct wakeup_info wi;
@@ -2988,7 +2822,7 @@
}
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
- if (debug_process_killing && lowmem_targets_size) {
+ if (debug_process_killing) {
ALOGI("Ignore %s memory pressure event "
"(free memory=%ldkB, cache=%ldkB, limit=%ldkB)",
level_name[level], other_free * page_k, other_file * page_k,
@@ -3052,7 +2886,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_process(level_oomadj[level], NULL, &mi, &wi, &curr_tm, NULL) == 0) {
+ if (find_and_kill_process(level_oomadj[level], NULL, &mi, &wi, &curr_tm) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -3075,7 +2909,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_process(min_score_adj, NULL, &mi, &wi, &curr_tm, NULL);
+ pages_freed = find_and_kill_process(min_score_adj, NULL, &mi, &wi, &curr_tm);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -3154,44 +2988,14 @@
mpevfd[level] = -1;
}
-enum class MemcgVersion {
- kNotFound,
- kV1,
- kV2,
-};
-
-static MemcgVersion __memcg_version() {
- std::string cgroupv2_path, memcg_path;
-
- if (!CgroupGetControllerPath("memory", &memcg_path)) {
- return MemcgVersion::kNotFound;
- }
- return CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroupv2_path) &&
- cgroupv2_path == memcg_path
- ? MemcgVersion::kV2
- : MemcgVersion::kV1;
-}
-
-static MemcgVersion memcg_version() {
- static MemcgVersion version = __memcg_version();
-
- return version;
-}
-
static bool init_psi_monitors() {
/*
* When PSI is used on low-ram devices or on high-end devices without memfree levels
- * use new kill strategy based on zone watermarks, free swap and thrashing stats.
- * Also use the new strategy if memcg has not been mounted in the v1 cgroups hiearchy since
- * the old strategy relies on memcg attributes that are available only in the v1 cgroups
- * hiearchy.
+ * use new kill strategy based on zone watermarks, free swap and thrashing stats
*/
bool use_new_strategy =
GET_LMK_PROPERTY(bool, "use_new_strategy", low_ram_device || !use_minfree_levels);
- if (!use_new_strategy && memcg_version() != MemcgVersion::kV1) {
- ALOGE("Old kill strategy can only be used with v1 cgroup hierarchy");
- return false;
- }
+
/* In default PSI mode override stall amounts using system properties */
if (use_new_strategy) {
/* Do not use low pressure level */
@@ -3216,13 +3020,6 @@
}
static bool init_mp_common(enum vmpressure_level level) {
- // The implementation of this function relies on memcg statistics that are only available in the
- // v1 cgroup hierarchy.
- if (memcg_version() != MemcgVersion::kV1) {
- ALOGE("%s: global monitoring is only available for the v1 cgroup hierarchy", __func__);
- return false;
- }
-
int mpfd;
int evfd;
int evctlfd;
@@ -3233,13 +3030,13 @@
const char *levelstr = level_name[level_idx];
/* gid containing AID_SYSTEM required */
- mpfd = open(GetCgroupAttributePath("MemPressureLevel").c_str(), O_RDONLY | O_CLOEXEC);
+ mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
goto err_open_mpfd;
}
- evctlfd = open(GetCgroupAttributePath("CgroupEventControl").c_str(), O_WRONLY | O_CLOEXEC);
+ evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
if (evctlfd < 0) {
ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
goto err_open_evctlfd;
@@ -3343,63 +3140,6 @@
}
}
-static void drop_reaper_comm() {
- close(reaper_comm_fd[0]);
- close(reaper_comm_fd[1]);
-}
-
-static bool setup_reaper_comm() {
- if (pipe(reaper_comm_fd)) {
- ALOGE("pipe failed: %s", strerror(errno));
- return false;
- }
-
- // Ensure main thread never blocks on read
- int flags = fcntl(reaper_comm_fd[0], F_GETFL);
- if (fcntl(reaper_comm_fd[0], F_SETFL, flags | O_NONBLOCK)) {
- ALOGE("fcntl failed: %s", strerror(errno));
- drop_reaper_comm();
- return false;
- }
-
- return true;
-}
-
-static bool init_reaper() {
- if (!reaper.is_reaping_supported()) {
- ALOGI("Process reaping is not supported");
- return false;
- }
-
- if (!setup_reaper_comm()) {
- ALOGE("Failed to create thread communication channel");
- return false;
- }
-
- // Setup epoll handler
- struct epoll_event epev;
- static struct event_handler_info kill_failed_hinfo = { 0, kill_fail_handler };
- epev.events = EPOLLIN;
- epev.data.ptr = (void *)&kill_failed_hinfo;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, reaper_comm_fd[0], &epev)) {
- ALOGE("epoll_ctl failed: %s", strerror(errno));
- drop_reaper_comm();
- return false;
- }
-
- if (!reaper.init(reaper_comm_fd[1])) {
- ALOGE("Failed to initialize reaper object");
- if (epoll_ctl(epollfd, EPOLL_CTL_DEL, reaper_comm_fd[0], &epev)) {
- ALOGE("epoll_ctl failed: %s", strerror(errno));
- }
- drop_reaper_comm();
- return false;
- }
- maxevents++;
-
- return true;
-}
-
static int init(void) {
static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
struct reread_data file_data = {
@@ -3517,7 +3257,6 @@
struct polling_params *poll_params, uint32_t events) {
struct timespec curr_tm;
- watchdog.start();
poll_params->update = POLLING_DO_NOT_CHANGE;
handler_info->handler(handler_info->data, events, poll_params);
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
@@ -3549,7 +3288,6 @@
}
break;
}
- watchdog.stop();
}
static void mainloop(void) {
@@ -3631,9 +3369,7 @@
if ((evt->events & EPOLLHUP) && evt->data.ptr) {
ALOGI("lmkd data connection dropped");
handler_info = (struct event_handler_info*)evt->data.ptr;
- watchdog.start();
ctrl_data_close(handler_info->data);
- watchdog.stop();
}
}
@@ -3718,18 +3454,14 @@
low_ram_device ? DEF_PARTIAL_STALL_LOWRAM : DEF_PARTIAL_STALL);
psi_complete_stall_ms = GET_LMK_PROPERTY(int32, "psi_complete_stall_ms",
DEF_COMPLETE_STALL);
- thrashing_limit_pct =
- std::max(0, GET_LMK_PROPERTY(int32, "thrashing_limit",
- low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING));
+ thrashing_limit_pct = max(0, GET_LMK_PROPERTY(int32, "thrashing_limit",
+ low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING));
thrashing_limit_decay_pct = clamp(0, 100, GET_LMK_PROPERTY(int32, "thrashing_limit_decay",
low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY));
- thrashing_critical_pct = std::max(
- 0, GET_LMK_PROPERTY(int32, "thrashing_limit_critical", thrashing_limit_pct * 2));
+ thrashing_critical_pct = max(0, GET_LMK_PROPERTY(int32, "thrashing_limit_critical",
+ thrashing_limit_pct * 2));
swap_util_max = clamp(0, 100, GET_LMK_PROPERTY(int32, "swap_util_max", 100));
filecache_min_kb = GET_LMK_PROPERTY(int64, "filecache_min_kb", 0);
- stall_limit_critical = GET_LMK_PROPERTY(int64, "stall_limit_critical", 100);
-
- reaper.enable_debug(debug_process_killing);
}
int main(int argc, char **argv) {
@@ -3771,15 +3503,6 @@
}
}
- if (init_reaper()) {
- ALOGI("Process reaper initialized with %d threads in the pool",
- reaper.thread_cnt());
- }
-
- if (!watchdog.init()) {
- ALOGE("Failed to initialize the watchdog");
- }
-
mainloop();
}
diff --git a/lmkd.rc b/lmkd.rc
index ba662b4..6f90bcb 100644
--- a/lmkd.rc
+++ b/lmkd.rc
@@ -5,7 +5,7 @@
capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
critical
socket lmkd seqpacket+passcred 0660 system system
- task_profiles ServiceCapacityLow
+ writepid /dev/cpuset/system-background/tasks
on property:lmkd.reinit=1
exec_background /system/bin/lmkd --reinit
diff --git a/reaper.cpp b/reaper.cpp
deleted file mode 100644
index 2c9e737..0000000
--- a/reaper.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2021 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "lowmemorykiller"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <log/log.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/epoll.h>
-#include <sys/pidfd.h>
-#include <sys/resource.h>
-#include <sys/sysinfo.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <processgroup/processgroup.h>
-#include <system/thread_defs.h>
-
-#include "reaper.h"
-
-#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
-#define THREAD_POOL_SIZE 2
-
-#ifndef __NR_process_mrelease
-#define __NR_process_mrelease 448
-#endif
-
-static int process_mrelease(int pidfd, unsigned int flags) {
- return syscall(__NR_process_mrelease, pidfd, flags);
-}
-
-static inline long get_time_diff_ms(struct timespec *from,
- struct timespec *to) {
- return (to->tv_sec - from->tv_sec) * (long)MS_PER_SEC +
- (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
-}
-
-static void* reaper_main(void* param) {
- Reaper *reaper = static_cast<Reaper*>(param);
- struct timespec start_tm, end_tm;
- struct Reaper::target_proc target;
- pid_t tid = gettid();
-
- // Ensure the thread does not use little cores
- if (!SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true)) {
- ALOGE("Failed to assign cpuset to the reaper thread");
- }
-
- for (;;) {
- target = reaper->dequeue_request();
-
- if (reaper->debug_enabled()) {
- clock_gettime(CLOCK_MONOTONIC_COARSE, &start_tm);
- }
-
- if (pidfd_send_signal(target.pidfd, SIGKILL, NULL, 0)) {
- // Inform the main thread about failure to kill
- reaper->notify_kill_failure(target.pid);
- goto done;
- }
- if (process_mrelease(target.pidfd, 0)) {
- ALOGE("process_mrelease %d failed: %s", target.pidfd, strerror(errno));
- goto done;
- }
- if (reaper->debug_enabled()) {
- clock_gettime(CLOCK_MONOTONIC_COARSE, &end_tm);
- ALOGI("Process %d was reaped in %ldms", target.pid,
- get_time_diff_ms(&start_tm, &end_tm));
- }
-done:
- close(target.pidfd);
- reaper->request_complete();
- }
-
- return NULL;
-}
-
-bool Reaper::is_reaping_supported() {
- static enum {
- UNKNOWN,
- SUPPORTED,
- UNSUPPORTED
- } reap_support = UNKNOWN;
-
- if (reap_support == UNKNOWN) {
- if (process_mrelease(-1, 0) && errno == ENOSYS) {
- reap_support = UNSUPPORTED;
- } else {
- reap_support = SUPPORTED;
- }
- }
- return reap_support == SUPPORTED;
-}
-
-bool Reaper::init(int comm_fd) {
- char name[16];
-
- if (thread_cnt_ > 0) {
- // init should not be called multiple times
- return false;
- }
-
- thread_pool_ = new pthread_t[THREAD_POOL_SIZE];
- for (int i = 0; i < THREAD_POOL_SIZE; i++) {
- if (pthread_create(&thread_pool_[thread_cnt_], NULL, reaper_main, this)) {
- ALOGE("pthread_create failed: %s", strerror(errno));
- continue;
- }
- snprintf(name, sizeof(name), "lmkd_reaper%d", thread_cnt_);
- if (pthread_setname_np(thread_pool_[thread_cnt_], name)) {
- ALOGW("pthread_setname_np failed: %s", strerror(errno));
- }
- thread_cnt_++;
- }
-
- if (!thread_cnt_) {
- delete[] thread_pool_;
- return false;
- }
-
- queue_.reserve(thread_cnt_);
- comm_fd_ = comm_fd;
- return true;
-}
-
-static void set_process_group_and_prio(uid_t uid, int pid, const std::vector<std::string>& profiles,
- int prio) {
- DIR* d;
- char proc_path[PATH_MAX];
- struct dirent* de;
-
- if (!SetProcessProfilesCached(uid, pid, profiles)) {
- ALOGW("Failed to set task profiles for the process (%d) being killed", pid);
- }
-
- snprintf(proc_path, sizeof(proc_path), "/proc/%d/task", pid);
- if (!(d = opendir(proc_path))) {
- ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno,
- pid);
- return;
- }
-
- while ((de = readdir(d))) {
- int t_pid;
-
- if (de->d_name[0] == '.') continue;
- t_pid = atoi(de->d_name);
-
- if (!t_pid) {
- ALOGW("Failed to get t_pid for '%s' of pid(%d)", de->d_name, pid);
- continue;
- }
-
- if (setpriority(PRIO_PROCESS, t_pid, prio) && errno != ESRCH) {
- ALOGW("Unable to raise priority of killing t_pid (%d): errno=%d", t_pid, errno);
- }
- }
- closedir(d);
-}
-
-bool Reaper::async_kill(const struct target_proc& target) {
- if (target.pidfd == -1) {
- return false;
- }
-
- if (!thread_cnt_) {
- return false;
- }
-
- mutex_.lock();
- if (active_requests_ >= thread_cnt_) {
- mutex_.unlock();
- return false;
- }
- active_requests_++;
-
- // Duplicate pidfd instead of reusing the original one to avoid synchronization and refcounting
- // when both reaper and main threads are using or closing the pidfd
- queue_.push_back({ dup(target.pidfd), target.pid, target.uid });
- // Wake up a reaper thread
- cond_.notify_one();
- mutex_.unlock();
-
- set_process_group_and_prio(target.uid, target.pid,
- {"CPUSET_SP_FOREGROUND", "SCHED_SP_FOREGROUND"},
- ANDROID_PRIORITY_HIGHEST);
-
- return true;
-}
-
-int Reaper::kill(const struct target_proc& target, bool synchronous) {
- /* CAP_KILL required */
- if (target.pidfd < 0) {
- return ::kill(target.pid, SIGKILL);
- }
-
- if (!synchronous && async_kill(target)) {
- // we assume the kill will be successful and if it fails we will be notified
- return 0;
- }
-
- int result = pidfd_send_signal(target.pidfd, SIGKILL, NULL, 0);
- if (result) {
- return result;
- }
-
- return is_reaping_supported() ? process_mrelease(target.pidfd, 0) : 0;
-}
-
-Reaper::target_proc Reaper::dequeue_request() {
- struct target_proc target;
- std::unique_lock<std::mutex> lock(mutex_);
-
- while (queue_.empty()) {
- cond_.wait(lock);
- }
- target = queue_.back();
- queue_.pop_back();
-
- return target;
-}
-
-void Reaper::request_complete() {
- std::scoped_lock<std::mutex> lock(mutex_);
- active_requests_--;
-}
-
-void Reaper::notify_kill_failure(int pid) {
- std::scoped_lock<std::mutex> lock(mutex_);
-
- ALOGE("Failed to kill process %d", pid);
- if (TEMP_FAILURE_RETRY(write(comm_fd_, &pid, sizeof(pid))) != sizeof(pid)) {
- ALOGE("thread communication write failed: %s", strerror(errno));
- }
-}
diff --git a/reaper.h b/reaper.h
deleted file mode 100644
index e20d892..0000000
--- a/reaper.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2021 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <condition_variable>
-#include <mutex>
-#include <vector>
-
-class Reaper {
-public:
- struct target_proc {
- int pidfd;
- int pid;
- uid_t uid;
- };
-private:
- // mutex_ and cond_ are used to wakeup the reaper thread.
- std::mutex mutex_;
- std::condition_variable cond_;
- // mutex_ protects queue_ and active_requests_ access.
- std::vector<struct target_proc> queue_;
- int active_requests_;
- // write side of the pipe to communicate kill failures with the main thread
- int comm_fd_;
- int thread_cnt_;
- pthread_t* thread_pool_;
- bool debug_enabled_;
-
- bool async_kill(const struct target_proc& target);
-public:
- Reaper() : active_requests_(0), thread_cnt_(0), debug_enabled_(false) {}
-
- static bool is_reaping_supported();
-
- bool init(int comm_fd);
- int thread_cnt() const { return thread_cnt_; }
- void enable_debug(bool enable) { debug_enabled_ = enable; }
- bool debug_enabled() const { return debug_enabled_; }
-
- // return 0 on success or error code returned by the syscall
- int kill(const struct target_proc& target, bool synchronous);
- // below members are used only by reaper_main
- target_proc dequeue_request();
- void request_complete();
- void notify_kill_failure(int pid);
-};
diff --git a/statslog.cpp b/statslog.cpp
index 26a6d86..6568f73 100644
--- a/statslog.cpp
+++ b/statslog.cpp
@@ -30,10 +30,6 @@
#include <time.h>
#include <unistd.h>
-#include <string>
-
-#include <processgroup/processgroup.h>
-
#ifdef LMKD_LOG_STATS
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
@@ -89,20 +85,18 @@
mem_st->swap_in_bytes = value;
}
-static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid __unused) {
- std::string path;
- if (!CgroupGetAttributePathForTask("MemStats", pid, &path)) {
- ALOGE("Querying MemStats path failed");
- return -1;
- }
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
+ FILE *fp;
+ char buf[PATH_MAX];
- FILE* fp = fopen(path.c_str(), "r");
+ snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
+
+ fp = fopen(buf, "r");
if (fp == NULL) {
return -1;
}
- char buf[PAGE_SIZE];
while (fgets(buf, PAGE_SIZE, fp) != NULL) {
memory_stat_parse_line(buf, mem_st);
}
diff --git a/statslog.h b/statslog.h
index e3f8b72..89e4d2e 100644
--- a/statslog.h
+++ b/statslog.h
@@ -92,6 +92,7 @@
#ifdef LMKD_LOG_STATS
+#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%d/memory.stat"
#define PROC_STAT_FILE_PATH "/proc/%d/stat"
#define PROC_STAT_BUFFER_SIZE 1024
#define BYTES_IN_KILOBYTE 1024
diff --git a/tests/Android.bp b/tests/Android.bp
index effdac7..dfbe0c7 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -23,7 +23,6 @@
"libbase",
"liblog",
"libcutils",
- "libprocessgroup",
],
static_libs: [
@@ -44,34 +43,3 @@
compile_multilib: "first",
}
-
-cc_test {
- name: "lmkd_tests",
- test_suites: ["device-tests"],
- require_root: true,
-
- shared_libs: [
- "libbase",
- "liblog",
- "libcutils",
- "libprocessgroup",
- ],
-
- static_libs: [
- "liblmkd_utils",
- ],
-
- target: {
- android: {
- srcs: ["lmkd_tests.cpp"],
- },
- },
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-
- compile_multilib: "first",
-}
diff --git a/tests/TEST_MAPPING b/tests/TEST_MAPPING
deleted file mode 100644
index 7c2533b..0000000
--- a/tests/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit-large": [
- {
- "name": "lmkd_tests"
- }
- ]
-}
diff --git a/tests/lmkd_tests.cpp b/tests/lmkd_tests.cpp
deleted file mode 100644
index 0676d85..0000000
--- a/tests/lmkd_tests.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <sstream>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <liblmkd_utils.h>
-#include <log/log_properties.h>
-#include <private/android_filesystem_config.h>
-
-using namespace android::base;
-
-#ifndef __NR_process_mrelease
-#define __NR_process_mrelease 448
-#endif
-
-#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
-
-#define LMKD_LOGCAT_MARKER "lowmemorykiller"
-#define LMKD_KILL_TEMPLATE "Kill \'[^']*\' \\\(%d\\)"
-#define LMKD_REAP_TEMPLATE "Process %d was reaped"
-
-#define LMKD_KILL_LINE_START LMKD_LOGCAT_MARKER ": Kill"
-#define LMKD_REAP_LINE_START LMKD_LOGCAT_MARKER ": Process"
-#define LMKD_REAP_TIME_TEMPLATE LMKD_LOGCAT_MARKER ": Process %d was reaped in %ldms"
-
-#define ONE_MB (1 << 20)
-
-// Test constant parameters
-#define OOM_ADJ_MAX 1000
-#define ALLOC_STEP (5 * ONE_MB)
-#define ALLOC_DELAY 200
-
-// used to create ptr aliasing and prevent compiler optimizing the access
-static volatile void* gptr;
-
-class LmkdTest : public ::testing::Test {
- public:
- virtual void SetUp() {
- // test requirements
- if (getuid() != static_cast<unsigned>(AID_ROOT)) {
- GTEST_SKIP() << "Must be root, skipping test";
- }
-
- if (!__android_log_is_debuggable()) {
- GTEST_SKIP() << "Must be userdebug build, skipping test";
- }
-
- if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
- GTEST_SKIP() << "Must not have kernel lowmemorykiller driver,"
- << " skipping test";
- }
-
- // should be able to turn on lmkd debug information
- if (!property_get_bool("ro.lmk.debug", true)) {
- GTEST_SKIP() << "Can't run with ro.lmk.debug property set to 'false', skipping test";
- }
-
- // setup lmkd connection
- ASSERT_FALSE((sock = lmkd_connect()) < 0)
- << "Failed to connect to lmkd process, err=" << strerror(errno);
-
- // enable ro.lmk.debug if not already enabled
- if (!property_get_bool("ro.lmk.debug", false)) {
- EXPECT_EQ(property_set("ro.lmk.debug", "true"), 0);
- EXPECT_EQ(lmkd_update_props(sock), UPDATE_PROPS_SUCCESS)
- << "Failed to reinitialize lmkd";
- }
-
- uid = getuid();
- }
-
- virtual void TearDown() {
- // drop lmkd connection
- close(sock);
- }
-
- void SetupChild(pid_t pid, int oomadj) {
- struct lmk_procprio params;
-
- params.pid = pid;
- params.uid = uid;
- params.oomadj = oomadj;
- params.ptype = PROC_TYPE_APP;
- ASSERT_FALSE(lmkd_register_proc(sock, ¶ms) < 0)
- << "Failed to communicate with lmkd, err=" << strerror(errno);
- GTEST_LOG_(INFO) << "Target process " << pid << " launched";
- if (property_get_bool("ro.config.low_ram", false)) {
- ASSERT_FALSE(create_memcg(uid, pid) != 0)
- << "Target process " << pid << " failed to create a cgroup";
- }
- }
-
- static std::string ExecCommand(const std::string& command) {
- FILE* fp = popen(command.c_str(), "r");
- std::string content;
- ReadFdToString(fileno(fp), &content);
- pclose(fp);
- return content;
- }
-
- static std::string ReadLogcat(const std::string& tag, const std::string& regex) {
- std::string cmd = "logcat -d -b all";
- if (!tag.empty()) {
- cmd += " -s \"" + tag + "\"";
- }
- if (!regex.empty()) {
- cmd += " -e \"" + regex + "\"";
- }
- return ExecCommand(cmd);
- }
-
- static size_t ConsumeMemory(size_t total_size, size_t step_size, size_t step_delay) {
- volatile void* ptr;
- size_t allocated_size = 0;
-
- while (allocated_size < total_size) {
- ptr = mmap(NULL, step_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (ptr != MAP_FAILED) {
- // create ptr aliasing to prevent compiler optimizing the access
- gptr = ptr;
- // make data non-zero
- memset((void*)ptr, (int)(allocated_size + 1), step_size);
- allocated_size += step_size;
- }
- usleep(step_delay);
- }
- return allocated_size;
- }
-
- static bool ParseProcSize(const std::string& line, long& rss, long& swap) {
- size_t pos = line.find("to free");
- if (pos == std::string::npos) {
- return false;
- }
- return sscanf(line.c_str() + pos, "to free %ldkB rss, %ldkB swap", &rss, &swap) == 2;
- }
-
- static bool ParseReapTime(const std::string& line, pid_t pid, long& reap_time) {
- int reap_pid;
- return sscanf(line.c_str(), LMKD_REAP_TIME_TEMPLATE, &reap_pid, &reap_time) == 2 &&
- reap_pid == pid;
- }
-
- private:
- int sock;
- uid_t uid;
-};
-
-TEST_F(LmkdTest, TargetReaping) {
- // test specific requirements
- if (syscall(__NR_process_mrelease, -1, 0) && errno == ENOSYS) {
- GTEST_SKIP() << "Must support process_mrelease syscall, skipping test";
- }
-
- // for a child to act as a target process
- pid_t pid = fork();
- ASSERT_FALSE(pid < 0) << "Failed to spawn a child process, err=" << strerror(errno);
- if (pid != 0) {
- // parent
- waitpid(pid, NULL, 0);
- } else {
- // child
- SetupChild(getpid(), OOM_ADJ_MAX);
- // allocate memory until killed
- ConsumeMemory((size_t)-1, ALLOC_STEP, ALLOC_DELAY);
- // should not reach here, child should be killed by OOM
- FAIL() << "Target process " << pid << " was not killed";
- }
-
- std::string regex =
- StringPrintf("((" LMKD_KILL_TEMPLATE ")|(" LMKD_REAP_TEMPLATE "))", pid, pid);
- std::string logcat_out = ReadLogcat(LMKD_LOGCAT_MARKER ":I", regex);
-
- // find kill report
- size_t line_start = logcat_out.find(LMKD_KILL_LINE_START);
- ASSERT_TRUE(line_start != std::string::npos) << "Kill report is not found";
- size_t line_end = logcat_out.find('\n', line_start);
- std::string line = logcat_out.substr(
- line_start, line_end == std::string::npos ? std::string::npos : line_end - line_start);
- long rss, swap;
- ASSERT_TRUE(ParseProcSize(line, rss, swap)) << "Kill report format is invalid";
-
- // find reap duration report
- line_start = logcat_out.find(LMKD_REAP_LINE_START, line_end);
- ASSERT_TRUE(line_start != std::string::npos) << "Reaping time report is not found";
- line_end = logcat_out.find('\n', line_start);
- line = logcat_out.substr(
- line_start, line_end == std::string::npos ? std::string::npos : line_end - line_start);
- long reap_time;
- ASSERT_TRUE(ParseReapTime(line, pid, reap_time) && reap_time > 0)
- << "Reaping time report format is invalid";
-
- double reclaim_speed = ((double)rss + swap) / reap_time;
- GTEST_LOG_(INFO) << "Reclaim speed " << reclaim_speed << "kB/ms (" << rss << "kB rss + " << swap
- << "kB swap) / " << reap_time << "ms";
-}
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- InitLogging(argv, StderrLogger);
- return RUN_ALL_TESTS();
-}
diff --git a/watchdog.cpp b/watchdog.cpp
deleted file mode 100644
index b1e4a03..0000000
--- a/watchdog.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2021 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "lowmemorykiller"
-
-#include <errno.h>
-#include <log/log.h>
-#include <string.h>
-
-#include <processgroup/processgroup.h>
-
-#include "watchdog.h"
-
-static void* watchdog_main(void* param) {
- Watchdog *watchdog = static_cast<Watchdog*>(param);
- sigset_t sigset;
- int signum;
-
- // Ensure the thread does not use little cores
- if (!SetTaskProfiles(gettid(), {"CPUSET_SP_FOREGROUND"}, true)) {
- ALOGE("Failed to assign cpuset to the watchdog thread");
- }
-
- if (!watchdog->create_timer(sigset)) {
- ALOGE("Watchdog timer creation failed!");
- return NULL;
- }
-
- while (true) {
- if (sigwait(&sigset, &signum) == -1) {
- ALOGE("sigwait failed: %s", strerror(errno));
- }
-
- watchdog->bite();
- }
-
- return NULL;
-}
-
-bool Watchdog::init() {
- pthread_t thread;
-
- if (pthread_create(&thread, NULL, watchdog_main, this)) {
- ALOGE("pthread_create failed: %s", strerror(errno));
- return false;
- }
- if (pthread_setname_np(thread, "lmkd_watchdog")) {
- ALOGW("pthread_setname_np failed: %s", strerror(errno));
- }
-
- return true;
-}
-
-bool Watchdog::start() {
- // Start the timer and keep it active until it's disarmed
- struct itimerspec new_timer;
-
- if (!timer_created_) {
- return false;
- }
-
- new_timer.it_value.tv_sec = timeout_;
- new_timer.it_value.tv_nsec = 0;
- new_timer.it_interval.tv_sec = timeout_;
- new_timer.it_interval.tv_nsec = 0;
-
- if (timer_settime(timer_, 0, &new_timer, NULL)) {
- ALOGE("timer_settime failed: %s", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-bool Watchdog::stop() {
- struct itimerspec new_timer = {};
-
- if (!timer_created_) {
- return false;
- }
-
- if (timer_settime(timer_, 0, &new_timer, NULL)) {
- ALOGE("timer_settime failed: %s", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-bool Watchdog::create_timer(sigset_t &sigset) {
- struct sigevent sevent;
-
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGALRM);
- if (sigprocmask(SIG_BLOCK, &sigset, NULL)) {
- ALOGE("sigprocmask failed: %s", strerror(errno));
- return false;
- }
-
- sevent.sigev_notify = SIGEV_THREAD_ID;
- sevent.sigev_notify_thread_id = gettid();
- sevent.sigev_signo = SIGALRM;
- if (timer_create(CLOCK_MONOTONIC, &sevent, &timer_)) {
- ALOGE("timer_create failed: %s", strerror(errno));
- return false;
- }
-
- timer_created_ = true;
- return true;
-}
diff --git a/watchdog.h b/watchdog.h
deleted file mode 100644
index 34cb602..0000000
--- a/watchdog.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2021 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-#include <signal.h>
-#include <time.h>
-
-class Watchdog {
-private:
- int timeout_;
- timer_t timer_;
- std::atomic<bool> timer_created_;
- void (*callback_)();
-public:
- Watchdog(int timeout, void (*callback)()) :
- timeout_(timeout), timer_created_(false), callback_(callback) {}
-
- bool init();
- bool start();
- bool stop();
- // used by the watchdog_main
- bool create_timer(sigset_t &sigset);
- void bite() const { if (callback_) callback_(); }
-};