1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Copyright (c) 2023 Huawei Device Co., Ltd.
4 */
5#include "ucollection_process_cpu.h"
6
7#include <asm/div64.h>
8#ifdef CONFIG_CPU_FREQ_TIMES
9#include <linux/cpufreq_times.h>
10#endif // CONFIG_CPU_FREQ_TIMES
11#include <linux/sched/stat.h>
12#include <linux/version.h>
13#include <linux/uaccess.h>
14#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
15#include <linux/sched.h>
16#include <linux/sched/cputime.h>
17#include <linux/sched/signal.h>
18#endif // LINUX_VERSION_CODE
19#ifdef CONFIG_SMT_MODE_GOV
20#include <platform_include/cee/linux/time_in_state.h>
21#endif // CONFIG_SMT_MODE_GOV
22
23#include "unified_collection_data.h"
24
25#define NS_TO_MS 1000000
26static char dmips_values[DMIPS_NUM];
27
28unsigned long long __attribute__((weak)) get_proc_cpu_load(struct task_struct *task, char dmips[],
29	unsigned int dmips_num)
30{
31	return 0;
32}
33
34static void get_process_flt(struct task_struct *task, struct ucollection_process_cpu_item* proc_cpu_entry)
35{
36	unsigned long tmp_min_flt = 0;
37	unsigned long tmp_maj_flt = 0;
38
39	struct task_struct *t = task;
40	signed int thread_count = 0;
41	do {
42		tmp_min_flt += t->min_flt;
43		tmp_maj_flt += t->maj_flt;
44		++thread_count;
45	} while_each_thread(task, t);
46
47	struct signal_struct *sig = task->signal;
48	if (sig != NULL) {
49		tmp_min_flt += sig->min_flt;
50		tmp_maj_flt += sig->maj_flt;
51	}
52
53	proc_cpu_entry->min_flt = tmp_min_flt;
54	proc_cpu_entry->maj_flt = tmp_maj_flt;
55	proc_cpu_entry->thread_total = thread_count;
56}
57
58static unsigned long long get_process_load_cputime(struct task_struct *task)
59{
60	unsigned long long proc_load_cputime = 0;
61	proc_load_cputime = get_proc_cpu_load(task, dmips_values, DMIPS_NUM);
62	return proc_load_cputime;
63}
64
65static void get_process_usage_cputime(struct task_struct *task, unsigned long long *ut, unsigned long long *st)
66{
67	unsigned long long utime, stime;
68
69	thread_group_cputime_adjusted(task, &utime, &stime);
70	do_div(utime, NS_TO_MS);
71	do_div(stime, NS_TO_MS);
72	*ut = utime;
73	*st = stime;
74}
75
76static void get_process_load(struct task_struct *task, int cur_count,
77	struct ucollection_process_cpu_entry __user *entry)
78{
79	struct ucollection_process_cpu_item proc_cpu_entry;
80	memset(&proc_cpu_entry, 0, sizeof(struct ucollection_process_cpu_item));
81	proc_cpu_entry.pid = task->pid;
82	get_process_flt(task, &proc_cpu_entry);
83	proc_cpu_entry.cpu_load_time = get_process_load_cputime(task);
84	get_process_usage_cputime(task, &proc_cpu_entry.cpu_usage_utime, &proc_cpu_entry.cpu_usage_stime);
85	(void)copy_to_user(&entry->datas[cur_count], &proc_cpu_entry, sizeof(struct ucollection_process_cpu_item));
86}
87
88static void get_thread_load(struct task_struct *task, int cur_count,
89	struct ucollection_thread_cpu_entry __user *entry)
90{
91	struct ucollection_thread_cpu_item thread_cpu_item;
92	memset(&thread_cpu_item, 0, sizeof(struct ucollection_thread_cpu_item));
93	unsigned long long utime, stime;
94	utime = task->utime;
95	stime = task->stime;
96	do_div(utime, NS_TO_MS);
97	do_div(stime, NS_TO_MS);
98	thread_cpu_item.tid = task->pid;
99	strcpy(thread_cpu_item.name, task->comm);
100	thread_cpu_item.cpu_usage_utime = utime;
101	thread_cpu_item.cpu_usage_stime = stime;
102	thread_cpu_item.cpu_load_time = 0;
103	(void)copy_to_user(&entry->datas[cur_count], &thread_cpu_item, sizeof(struct ucollection_thread_cpu_item));
104}
105
106static long ioctrl_collect_process_cpu(void __user *argp)
107{
108	struct task_struct *task = NULL;
109	struct ucollection_process_cpu_entry kentry;
110	struct ucollection_process_cpu_entry __user *entry = argp;
111	if (entry == NULL) {
112		pr_err("cpu entry is null");
113		return -EINVAL;
114	}
115
116	memset(&kentry, 0, sizeof(struct ucollection_process_cpu_entry));
117	(void)copy_from_user(&kentry, entry, sizeof(struct ucollection_process_cpu_entry));
118
119	rcu_read_lock();
120	task = &init_task;
121	for_each_process(task) {
122		if (task->pid != task->tgid)
123			continue;
124
125		if (kentry.cur_count >= kentry.total_count) {
126			pr_err("process over total count");
127			break;
128		}
129
130		get_process_load(task, kentry.cur_count, entry);
131		kentry.cur_count++;
132	}
133	put_user(kentry.cur_count, &entry->cur_count);
134	rcu_read_unlock();
135	return 0;
136}
137
138static struct task_struct* get_alive_task_by_pid(unsigned int pid)
139{
140	struct task_struct *task = NULL;
141	task = find_task_by_pid_ns(pid, &init_pid_ns);
142	if (task == NULL || !pid_alive(task)) {
143		return NULL;
144	}
145	return task;
146}
147
148static long ioctrl_collect_process_count(void __user *argp)
149{
150	struct task_struct *task = NULL;
151	unsigned int process_count = 0;
152	unsigned int __user *count = argp;
153	rcu_read_lock();
154	task = &init_task;
155	for_each_process(task) {
156		if (task->pid != task->tgid) {
157			continue;
158		}
159		++process_count;
160	}
161	rcu_read_unlock();
162	put_user(process_count, count);
163	return 0;
164}
165
166static long read_thread_count_locked(struct ucollection_process_thread_count *kcount,
167	struct ucollection_process_thread_count __user *count)
168{
169	rcu_read_lock();
170	struct task_struct *task = get_alive_task_by_pid(kcount->pid);
171	if (task == NULL) {
172		pr_info("pid=%d is task NULL or not alive", kcount->pid);
173		rcu_read_unlock();
174		return -EINVAL;
175	}
176	unsigned int thread_count = 0;
177	struct task_struct *t = task;
178	do {
179		thread_count++;
180	} while_each_thread(task, t);
181	put_user(thread_count, &count->thread_count);
182	rcu_read_unlock();
183	return 0;
184}
185
186static long ioctrl_collect_thread_count(void __user *argp)
187{
188	struct ucollection_process_thread_count kcount;
189	struct ucollection_process_thread_count __user *count = argp;
190	if (count == NULL) {
191		pr_err("cpu entry is null");
192		return -EINVAL;
193	}
194	memset(&kcount, 0, sizeof(struct ucollection_process_thread_count));
195	(void)copy_from_user(&kcount, count, sizeof(struct ucollection_process_thread_count));
196	return read_thread_count_locked(&kcount, count);
197}
198
199static long ioctrl_collect_app_thread_count(void __user *argp)
200{
201	struct ucollection_process_thread_count kcount;
202	struct ucollection_process_thread_count __user *count = argp;
203	if (count == NULL) {
204		pr_err("cpu entry is null");
205		return -EINVAL;
206	}
207	memset(&kcount, 0, sizeof(struct ucollection_process_thread_count));
208	(void)copy_from_user(&kcount, count, sizeof(struct ucollection_process_thread_count));
209	if (current->tgid != kcount.pid) {
210		pr_err("pid=%d is not self current tgid:%d", kcount.pid, current->tgid);
211		return -EINVAL;
212	}
213	return read_thread_count_locked(&kcount, count);
214}
215
216static long read_thread_info_locked(struct ucollection_thread_cpu_entry *kentry,
217	struct ucollection_thread_cpu_entry __user *entry)
218{
219	rcu_read_lock();
220	struct task_struct *task = get_alive_task_by_pid(kentry->filter.pid);
221	if (task == NULL) {
222		pr_info("pid=%d is task NULL not alive", kentry->filter.pid);
223		rcu_read_unlock();
224		return -EINVAL;
225	}
226	unsigned int thread_count = 0;
227	struct task_struct *t = task;
228	do {
229		if (thread_count >= kentry->total_count) {
230			pr_err("thread over total count");
231			break;
232		}
233		get_thread_load(t, thread_count, entry);
234		thread_count++;
235	} while_each_thread(task, t);
236	put_user(thread_count, &entry->cur_count);
237	rcu_read_unlock();
238	return 0;
239}
240
241static long ioctrl_collect_app_thread_cpu(void __user *argp)
242{
243	struct ucollection_thread_cpu_entry kentry;
244	struct ucollection_thread_cpu_entry __user *entry = argp;
245	if (entry == NULL) {
246		pr_err("cpu entry is null");
247		return -EINVAL;
248	}
249	memset(&kentry, 0, sizeof(struct ucollection_thread_cpu_entry));
250	(void)copy_from_user(&kentry, entry, sizeof(struct ucollection_thread_cpu_entry));
251	if (current->tgid != kentry.filter.pid || kentry.cur_count >= kentry.total_count) {
252		pr_err("pid=%d is not self current tgid:%d , or current count over total count"
253			, kentry.filter.pid, current->tgid);
254		return -EINVAL;
255	}
256	return read_thread_info_locked(&kentry, entry);
257}
258
259static long ioctrl_collect_the_thread_cpu(void __user *argp)
260{
261	struct ucollection_thread_cpu_entry kentry;
262	struct ucollection_thread_cpu_entry __user *entry = argp;
263	if (entry == NULL) {
264		pr_err("cpu entry is null");
265		return -EINVAL;
266	}
267	memset(&kentry, 0, sizeof(struct ucollection_thread_cpu_entry));
268	(void)copy_from_user(&kentry, entry, sizeof(struct ucollection_thread_cpu_entry));
269	if (kentry.cur_count >= kentry.total_count) {
270		pr_err("pid=%d is not self current:%d , or current count over total count"
271			, kentry.filter.pid, current->pid);
272		return -EINVAL;
273	}
274	return read_thread_info_locked(&kentry, entry);
275}
276
277static long ioctrl_collect_the_process_cpu(void __user *argp)
278{
279	struct ucollection_process_cpu_entry kentry;
280	struct ucollection_process_cpu_entry __user *entry = argp;
281	if (entry == NULL) {
282		pr_err("cpu entry is null");
283		return -EINVAL;
284	}
285
286	memset(&kentry, 0, sizeof(struct ucollection_process_cpu_entry));
287	(void)copy_from_user(&kentry, entry, sizeof(struct ucollection_process_cpu_entry));
288
289	if (kentry.cur_count >= kentry.total_count) {
290		pr_err("current count over total count");
291		return -EINVAL;
292	}
293
294	rcu_read_lock();
295	struct task_struct *task = get_alive_task_by_pid(kentry.filter.pid);
296	if (task == NULL) {
297		pr_info("pid=%d is task null or not alive", kentry.filter.pid);
298		rcu_read_unlock();
299		return -EINVAL;
300	}
301
302	get_process_load(task, kentry.cur_count, entry);
303	kentry.cur_count++;
304	put_user(kentry.cur_count, &entry->cur_count);
305	rcu_read_unlock();
306	return 0;
307}
308
309long unified_collection_collect_process_cpu(unsigned int cmd, void __user *argp)
310{
311	long ret = 0;
312	switch(cmd) {
313	case IOCTRL_COLLECT_ALL_PROC_CPU:
314		ret = ioctrl_collect_process_cpu(argp);
315		break;
316	case IOCTRL_COLLECT_THE_PROC_CPU:
317		ret = ioctrl_collect_the_process_cpu(argp);
318		break;
319	case IOCTRL_COLLECT_THREAD_COUNT:
320		ret = ioctrl_collect_thread_count(argp);
321		break;
322	case IOCTRL_COLLECT_APP_THREAD_COUNT:
323		ret = ioctrl_collect_app_thread_count(argp);
324		break;
325	case IOCTRL_COLLECT_APP_THREAD:
326		ret = ioctrl_collect_app_thread_cpu(argp);
327		break;
328	case IOCTRL_COLLECT_THE_THREAD:
329		ret = ioctrl_collect_the_thread_cpu(argp);
330		break;
331	case IOCTRL_COLLECT_PROC_COUNT:
332		ret = ioctrl_collect_process_count(argp);
333		break;
334	default:
335		pr_err("handle ioctrl cmd %u, _IOC_TYPE(cmd)=%d", cmd, _IOC_TYPE(cmd));
336		ret = 0;
337	}
338	return ret;
339}