aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Blackler <jimblackler@google.com>2019-04-05 16:25:04 +0100
committerJim Blackler <jimblackler@google.com>2019-05-01 13:31:50 +0000
commit152bacdd85c46f0c76b00c4acc253e414513634c (patch)
treecef12be7301064cef8b40914e183786dd6a88148
parentaab9adb4b81bcce107dd1783f6ac41dc1abe9f15 (diff)
ANDROID: Communicates LMK events to userland where they can be loggedASB-2019-06-05_4.4ASB-2019-05-05_4.4
Makes a file /proc/lowmemorykiller available for polling by user space. Immediately as a process is killed by lowmemorykiller information about that process is pushed to the file. It contains information about the process at the time it was killed, to be gathered as metrics. The file is expected to be continuously read from. To prevent failure to do so from causing memory to be continuously consumed, after eight processes have been killed, no more events will be logged until the file is read from again. The format is integers separated by spaces: Field 0 pid The identifier of the process. Field 1 uid The UNIX ID of the user the process was running under. Field 2 group_leader_pid The group leader of the process. Field 3 min_flt The number of minor page faults. Field 4 maj_flt The number of minor page faults. Field 5 rss_in_pages The Resident Set Size in pages. Field 6 oom_score_adj The adjusted Out Of Memory score. Field 7 start_time The number of nanoseconds that the process ran for. --New line-- Field 8 taskname The cmd line used to launch the process, truncated to 127 characters, and terminated by a new line. Bug: 130017100 Test: Tested manually Change-Id: I461b191eda9e578b9d2c2d1843b1b81948353a95 Signed-off-by: Jim Blackler <jimblackler@google.com>
-rw-r--r--drivers/staging/android/lowmemorykiller.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 2f4ef2120d31..8b19b5e4bd69 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -42,6 +42,9 @@
#include <linux/rcupdate.h>
#include <linux/profile.h>
#include <linux/notifier.h>
+#include <linux/circ_buf.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
#define CREATE_TRACE_POINTS
#include "trace/lowmemorykiller.h"
@@ -70,6 +73,157 @@ static unsigned long lowmem_deathpending_timeout;
pr_info(x); \
} while (0)
+
+static DECLARE_WAIT_QUEUE_HEAD(event_wait);
+static DEFINE_SPINLOCK(lmk_event_lock);
+static struct circ_buf event_buffer;
+#define MAX_BUFFERED_EVENTS 8
+#define MAX_TASKNAME 128
+
+struct lmk_event {
+ char taskname[MAX_TASKNAME];
+ pid_t pid;
+ uid_t uid;
+ pid_t group_leader_pid;
+ unsigned long min_flt;
+ unsigned long maj_flt;
+ unsigned long rss_in_pages;
+ short oom_score_adj;
+ short min_score_adj;
+ unsigned long long start_time;
+ struct list_head list;
+};
+
+void handle_lmk_event(struct task_struct *selected, short min_score_adj)
+{
+ int head;
+ int tail;
+ struct lmk_event *events;
+ struct lmk_event *event;
+ int res;
+ long rss_in_pages = -1;
+ struct mm_struct *mm = get_task_mm(selected);
+
+ if (mm) {
+ rss_in_pages = get_mm_rss(mm);
+ mmput(mm);
+ }
+
+ spin_lock(&lmk_event_lock);
+
+ head = event_buffer.head;
+ tail = READ_ONCE(event_buffer.tail);
+
+ /* Do not continue to log if no space remains in the buffer. */
+ if (CIRC_SPACE(head, tail, MAX_BUFFERED_EVENTS) < 1) {
+ spin_unlock(&lmk_event_lock);
+ return;
+ }
+
+ events = (struct lmk_event *) event_buffer.buf;
+ event = &events[head];
+
+ res = get_cmdline(selected, event->taskname, MAX_TASKNAME - 1);
+
+ /* No valid process name means this is definitely not associated with a
+ * userspace activity.
+ */
+
+ if (res <= 0 || res >= MAX_TASKNAME) {
+ spin_unlock(&lmk_event_lock);
+ return;
+ }
+
+ event->taskname[res] = '\0';
+ event->pid = selected->pid;
+ event->uid = from_kuid_munged(current_user_ns(), task_uid(selected));
+ if (selected->group_leader)
+ event->group_leader_pid = selected->group_leader->pid;
+ else
+ event->group_leader_pid = -1;
+ event->min_flt = selected->min_flt;
+ event->maj_flt = selected->maj_flt;
+ event->oom_score_adj = selected->signal->oom_score_adj;
+ event->start_time = nsec_to_clock_t(selected->real_start_time);
+ event->rss_in_pages = rss_in_pages;
+ event->min_score_adj = min_score_adj;
+
+ event_buffer.head = (head + 1) & (MAX_BUFFERED_EVENTS - 1);
+
+ spin_unlock(&lmk_event_lock);
+
+ wake_up_interruptible(&event_wait);
+}
+
+static int lmk_event_show(struct seq_file *s, void *unused)
+{
+ struct lmk_event *events = (struct lmk_event *) event_buffer.buf;
+ int head;
+ int tail;
+ struct lmk_event *event;
+
+ spin_lock(&lmk_event_lock);
+
+ head = event_buffer.head;
+ tail = event_buffer.tail;
+
+ if (head == tail) {
+ spin_unlock(&lmk_event_lock);
+ return -EAGAIN;
+ }
+
+ event = &events[tail];
+
+ seq_printf(s, "%lu %lu %lu %lu %lu %lu %hd %hd %llu\n%s\n",
+ (unsigned long) event->pid, (unsigned long) event->uid,
+ (unsigned long) event->group_leader_pid, event->min_flt,
+ event->maj_flt, event->rss_in_pages, event->oom_score_adj,
+ event->min_score_adj, event->start_time, event->taskname);
+
+ event_buffer.tail = (tail + 1) & (MAX_BUFFERED_EVENTS - 1);
+
+ spin_unlock(&lmk_event_lock);
+ return 0;
+}
+
+static unsigned int lmk_event_poll(struct file *file, poll_table *wait)
+{
+ int ret = 0;
+
+ poll_wait(file, &event_wait, wait);
+ spin_lock(&lmk_event_lock);
+ if (event_buffer.head != event_buffer.tail)
+ ret = POLLIN;
+ spin_unlock(&lmk_event_lock);
+ return ret;
+}
+
+static int lmk_event_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lmk_event_show, inode->i_private);
+}
+
+static const struct file_operations event_file_ops = {
+ .open = lmk_event_open,
+ .poll = lmk_event_poll,
+ .read = seq_read
+};
+
+static void lmk_event_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ event_buffer.head = 0;
+ event_buffer.tail = 0;
+ event_buffer.buf = kmalloc(
+ sizeof(struct lmk_event) * MAX_BUFFERED_EVENTS, GFP_KERNEL);
+ if (!event_buffer.buf)
+ return;
+ entry = proc_create("lowmemorykiller", 0, NULL, &event_file_ops);
+ if (!entry)
+ pr_err("error creating kernel lmk event file\n");
+}
+
static unsigned long lowmem_count(struct shrinker *s,
struct shrink_control *sc)
{
@@ -190,6 +344,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
free);
lowmem_deathpending_timeout = jiffies + HZ;
rem += selected_tasksize;
+
+ handle_lmk_event(selected, min_score_adj);
}
lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
@@ -207,6 +363,7 @@ static struct shrinker lowmem_shrinker = {
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
+ lmk_event_init();
return 0;
}
device_initcall(lowmem_init);