Support Linux kernel v5.10
[platform/core/system/stability-monitor-kmod-rpi3.git] / kernel / proc-tsm.c
1 /*
2  * This file is part of stability-monitor-kmod-rpi3
3  *
4  * Copyright © 2019 Samsung Electronics
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see http://www.gnu.org/licenses/
18  */
19
20 #include <linux/module.h>
21 #include <linux/moduleparam.h>
22 #include <linux/init.h>
23 #include <linux/kernel.h>
24 #include <linux/version.h>
25
26 #include <linux/fs.h>
27 #include <linux/proc_fs.h>
28 #include <linux/seq_file.h>
29 #include <linux/sched.h>
30 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
31 #include <linux/sched/signal.h>
32 #include <linux/sched/cputime.h>
33 #endif
34 #include <linux/mm.h>
35 #include <linux/pid_namespace.h>
36 #include <asm/uaccess.h>
37 #include <linux/time.h>
38 #include <linux/posix-timers.h>
39 #include <linux/kernel_stat.h>
40 #include <linux/fdtable.h>
41 #include <linux/math64.h>
42 #include <linux/task_io_accounting_ops.h>
43 #include <linux/security.h>
44
45 #define DEBUG 0
46 #define PAGE_TO_KB(x) ((x) << (PAGE_SHIFT - 10))
47
48 static struct proc_dir_entry* proc_file;
49 static char *blacklist[10] = {0, };
50 static int blacklist_c = 0;
51
52 module_param_array(blacklist, charp, &blacklist_c, 0000);
53
54 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
55 /* ktime_get_boot_ns() is renamed to ktime_get_boottime_ns() from v5.3 */
56 static inline u64 ktime_get_boot_ns(void) {
57         return ktime_get_boottime_ns();
58 }
59 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) */
60
61 static u64 time_now(void)
62 {
63     return div64_ul(ktime_get_boot_ns(), NSEC_PER_USEC);
64 }
65
66 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
67 static inline u64 cputime_to_usecs(const u64 cputime)
68 {
69     // return cputime >> 12;
70     return div64_ul(cputime, NSEC_PER_USEC);
71 }
72 #endif
73
74 static unsigned int count_open_files(struct files_struct *files)
75 {
76     unsigned int open_files = 0;
77     int i;
78     struct fdtable *fdt;
79
80     if (!files)
81         return 0;
82
83     spin_lock(&files->file_lock);
84     fdt = files_fdtable(files);
85
86     /* Find the last non zero cell in open fds array */
87     for (i = fdt->max_fds / BITS_PER_LONG - 1; i >= 0; i--) {
88         if (fdt->open_fds[i])
89             break;
90     }
91
92     /* Count set bits */
93     for (i = (i + 1) * BITS_PER_LONG - 1; i >= 0; i--) {
94         if(test_bit(i, fdt->open_fds))
95             open_files++;
96     }
97
98     spin_unlock(&files->file_lock);
99     return open_files;
100 }
101
102 static int stability_monitor_show(struct seq_file *m, void *v)
103 {
104     struct sysinfo info;
105     struct task_struct *task, *t;
106     struct pid *pid = NULL;
107     pid_t ppid = 0;
108 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
109     u64 uptime, utime, stime, ut, st;
110 #else
111     cputime_t uptime, utime, stime, ut, st;
112 #endif
113     unsigned long long vm_rss, total_ram;
114     struct task_io_accounting task_ioac;
115     unsigned int open_fds;
116     u32 secid;
117     char *label;
118     u32 label_len;
119     int blacklisted;
120     int i;
121
122     rcu_read_lock();
123
124     /* Uptime in us */
125     uptime = time_now();
126
127     /* Memory info */
128     si_meminfo(&info);
129     total_ram = PAGE_TO_KB(info.totalram);
130
131 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
132     seq_put_decimal_ull(m, "", uptime);
133     seq_put_decimal_ull(m, " ", total_ram);
134 #else
135     seq_put_decimal_ull(m, 0, uptime);
136     seq_put_decimal_ull(m, ' ', total_ram);
137 #endif
138     seq_printf(m, "\n");
139
140     for_each_process(task) {
141         ppid = task->real_parent->pid;
142         pid = task_pgrp(task);
143         if (!pid)
144             continue;
145
146         task_lock(task);
147
148         if (!task->mm) {
149             task_unlock(task);
150             continue;
151         }
152
153         if (task->flags & (PF_DUMPCORE|PF_SIGNALED|PF_EXITING)) {
154             task_unlock(task);
155             continue;
156         }
157
158 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
159         security_cred_getsecid(((struct cred *)task->real_cred), &secid);
160 #else
161         security_task_getsecid(task, &secid);
162 #endif
163         if (!security_secid_to_secctx(secid, &label, &label_len)) {
164             blacklisted = 0;
165             for (i = 0; i < blacklist_c; i++) {
166                 if (strcmp(label, blacklist[i]) == 0) {
167                     blacklisted = 1;
168                     break;
169                 }
170             }
171             if (blacklisted) {
172                 task_unlock(task);
173                 continue;
174             }
175         }
176
177         /* Memory */
178         vm_rss = get_mm_rss(task->mm);
179
180         /* Open FDs */
181         open_fds = count_open_files(task->files);
182
183         /* CPU time + IO accounting */
184         task_cputime_adjusted(task, &utime, &stime);
185         task_ioac = task->ioac;
186         t = task;
187         while_each_thread(task, t) {
188             task_cputime_adjusted(t, &ut, &st);
189             utime += ut;
190             stime += st;
191             task_io_accounting_add(&task_ioac, &t->ioac);
192         }
193
194         task_unlock(task);
195
196 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
197         seq_put_decimal_ll(m, "", task->pid);
198         seq_put_decimal_ll(m, " ", ppid);
199         seq_put_decimal_ull(m, " ", cputime_to_usecs(utime + stime));
200         seq_put_decimal_ull(m, " ", PAGE_TO_KB(vm_rss));
201         seq_put_decimal_ll(m, " ", open_fds);
202 #ifdef CONFIG_TASK_IO_ACCOUNTING
203         seq_put_decimal_ull(m, " ", task_ioac.read_bytes + task_ioac.write_bytes);
204 #else
205         seq_put_decimal_ull(m, " ", 0);
206 #endif
207 #else   /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) */
208         seq_put_decimal_ll(m, 0, task->pid);
209         seq_put_decimal_ll(m, ' ', ppid);
210         seq_put_decimal_ull(m, ' ', cputime_to_usecs(utime + stime));
211         seq_put_decimal_ull(m, ' ', PAGE_TO_KB(vm_rss));
212         seq_put_decimal_ll(m, ' ', open_fds);
213 #ifdef CONFIG_TASK_IO_ACCOUNTING
214         seq_put_decimal_ull(m, ' ', task_ioac.read_bytes + task_ioac.write_bytes);
215 #else
216         seq_put_decimal_ull(m, ' ', 0);
217 #endif
218 #endif  /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) */
219         seq_printf(m, "\n");
220     }
221
222     rcu_read_unlock();
223
224 #if DEBUG
225     printk(KERN_DEBUG "stability_monitor_show() took %llu us\n", time_now() - uptime);
226 #endif
227
228     return 0;
229 }
230
231 static int stability_monitor_open(struct inode *inode, struct file *file)
232 {
233     return single_open_size(file, stability_monitor_show, NULL, 8192);
234 }
235
236 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
237 static const struct proc_ops stability_monitor_fops = {
238     .proc_open = stability_monitor_open,
239     .proc_read = seq_read,
240     .proc_lseek = seq_lseek,
241     .proc_release = single_release,
242 };
243 #else
244 static const struct file_operations stability_monitor_fops = {
245     .owner      = THIS_MODULE,
246     .open       = stability_monitor_open,
247     .read       = seq_read,
248     .llseek  = seq_lseek,
249     .release = single_release,
250 };
251 #endif
252
253 static int __init stability_monitor_init(void)
254 {
255     proc_file = proc_create("tsm", 0, NULL, &stability_monitor_fops);
256     if (!proc_file)
257         return -ENOMEM;
258
259     return 0;
260 }
261
262 static void __exit stability_monitor_exit(void)
263 {
264     remove_proc_entry("tsm", NULL);
265 }
266
267 module_init(stability_monitor_init);
268 module_exit(stability_monitor_exit);
269
270 MODULE_LICENSE("GPL");