1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2023 Huawei Technologies Co., Ltd. 4 */ 5 6#include <linux/fs.h> 7#include <linux/proc_fs.h> 8#include <linux/seq_file.h> 9#include <linux/fdtable.h> 10#include <linux/sched/task.h> 11#include <linux/sched/signal.h> 12#include "../drivers/staging/android/ashmem.h" 13 14#define PURGEABLE_ASHMEM_SHRINKALL_ARG 0 15 16struct purgeable_ashmem_trigger_args { 17 struct seq_file *seq; 18 struct task_struct *tsk; 19}; 20 21static int purgeable_ashmem_trigger_cb(const void *data, 22 struct file *f, unsigned int fd) 23{ 24 const struct purgeable_ashmem_trigger_args *args = data; 25 struct task_struct *tsk = args->tsk; 26 struct purgeable_ashmem_metadata pmdata; 27 28 if (!is_ashmem_file(f)) 29 return 0; 30 if (!get_purgeable_ashmem_metadata(f, &pmdata)) 31 return 0; 32 if (pmdata.is_purgeable) { 33 pmdata.name = pmdata.name == NULL ? "" : pmdata.name; 34 seq_printf(args->seq, 35 "%s,%u,%u,%ld,%s,%zu,%u,%u,%d,%d\n", 36 tsk->comm, tsk->pid, fd, (long)tsk->signal->oom_score_adj, 37 pmdata.name, pmdata.size, pmdata.id, pmdata.create_time, 38 pmdata.refc, pmdata.purged); 39 } 40 return 0; 41} 42 43static ssize_t purgeable_ashmem_trigger_write(struct file *file, 44 const char __user *buffer, size_t count, loff_t *ppos) 45{ 46 char *buf; 47 unsigned int ashmem_id = 0; 48 unsigned int create_time = 0; 49 const unsigned int params_num = 2; 50 const struct cred *cred = current_cred(); 51 52 if (!cred) 53 return 0; 54 55 if (!uid_eq(cred->euid, GLOBAL_MEMMGR_UID) && 56 !uid_eq(cred->euid, GLOBAL_ROOT_UID)) { 57 pr_err("no permission to shrink purgeable ashmem!\n"); 58 return 0; 59 } 60 buf = memdup_user_nul(buffer, count); 61 buf = strstrip(buf); 62 if (sscanf(buf, "%u %u", &ashmem_id, &create_time) != params_num) 63 return -EINVAL; 64 if (ashmem_id == PURGEABLE_ASHMEM_SHRINKALL_ARG && 65 create_time == PURGEABLE_ASHMEM_SHRINKALL_ARG) 66 ashmem_shrinkall(); 67 else 68 ashmem_shrink_by_id(ashmem_id, create_time); 69 return count; 70} 71 72static int purgeable_ashmem_trigger_show(struct seq_file *s, void *d) 73{ 74 struct task_struct *tsk = NULL; 75 struct purgeable_ashmem_trigger_args cb_args; 76 const struct cred *cred = current_cred(); 77 78 if (!cred) 79 return -EINVAL; 80 81 if (!uid_eq(cred->euid, GLOBAL_MEMMGR_UID) && 82 !uid_eq(cred->euid, GLOBAL_ROOT_UID)) { 83 pr_err("no permission to shrink purgeable ashmem!\n"); 84 return -EINVAL; 85 } 86 seq_puts(s, "Process purgeable ashmem detail info:\n"); 87 seq_puts(s, "----------------------------------------------------\n"); 88 seq_printf(s, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", 89 "process_name", "pid", "adj", "fd", 90 "ashmem_name", "size", "id", "time", "ref_count", "purged"); 91 92 ashmem_mutex_lock(); 93 rcu_read_lock(); 94 for_each_process(tsk) { 95 if (tsk->flags & PF_KTHREAD) 96 continue; 97 cb_args.seq = s; 98 cb_args.tsk = tsk; 99 100 task_lock(tsk); 101 iterate_fd(tsk->files, 0, 102 purgeable_ashmem_trigger_cb, (void *)&cb_args); 103 task_unlock(tsk); 104 } 105 rcu_read_unlock(); 106 ashmem_mutex_unlock(); 107 seq_puts(s, "----------------------------------------------------\n"); 108 return 0; 109} 110 111static int purgeable_ashmem_trigger_open(struct inode *inode, 112 struct file *file) 113{ 114 return single_open(file, purgeable_ashmem_trigger_show, 115 inode->i_private); 116} 117 118static const struct proc_ops purgeable_ashmem_trigger_fops = { 119 .proc_open = purgeable_ashmem_trigger_open, 120 .proc_write = purgeable_ashmem_trigger_write, 121 .proc_read = seq_read, 122 .proc_lseek = seq_lseek, 123 .proc_release = single_release, 124}; 125 126void init_purgeable_ashmem_trigger(void) 127{ 128 struct proc_dir_entry *entry = NULL; 129 130 entry = proc_create_data("purgeable_ashmem_trigger", 0666, 131 NULL, &purgeable_ashmem_trigger_fops, NULL); 132 if (!entry) 133 pr_err("Failed to create purgeable ashmem trigger\n"); 134} 135