diff options
| -rw-r--r-- | drivers/staging/android/lowmemorykiller.c | 157 |
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); |
