• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 struct purgeable_ashmem_trigger_args {
17 	struct seq_file *seq;
18 	struct task_struct *tsk;
19 };
20 
purgeable_ashmem_trigger_cb(const void * data,struct file * f,unsigned int fd)21 static 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 
purgeable_ashmem_trigger_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)43 static 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 
purgeable_ashmem_trigger_show(struct seq_file * s,void * d)72 static 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 
purgeable_ashmem_trigger_open(struct inode * inode,struct file * file)111 static 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 
118 static 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 
init_purgeable_ashmem_trigger(void)126 void 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