Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
4 */
5
6#include <proc/readproc.h>
7#include <stdarg.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include <ctype.h>
12#include <errno.h>
13#include <fcntl.h>
14#include <sched.h>
15#include <stdio.h>
16
17#include "utils.h"
18
19#define MAX_MSG_LENGTH 1024
20int config_debug;
21
22/*
23 * err_msg - print an error message to the stderr
24 */
25void err_msg(const char *fmt, ...)
26{
27 char message[MAX_MSG_LENGTH];
28 va_list ap;
29
30 va_start(ap, fmt);
31 vsnprintf(message, sizeof(message), fmt, ap);
32 va_end(ap);
33
34 fprintf(stderr, "%s", message);
35}
36
37/*
38 * debug_msg - print a debug message to stderr if debug is set
39 */
40void debug_msg(const char *fmt, ...)
41{
42 char message[MAX_MSG_LENGTH];
43 va_list ap;
44
45 if (!config_debug)
46 return;
47
48 va_start(ap, fmt);
49 vsnprintf(message, sizeof(message), fmt, ap);
50 va_end(ap);
51
52 fprintf(stderr, "%s", message);
53}
54
55/*
56 * get_llong_from_str - get a long long int from a string
57 */
58long long get_llong_from_str(char *start)
59{
60 long long value;
61 char *end;
62
63 errno = 0;
64 value = strtoll(start, &end, 10);
65 if (errno || start == end)
66 return -1;
67
68 return value;
69}
70
71/*
72 * get_duration - fill output with a human readable duration since start_time
73 */
74void get_duration(time_t start_time, char *output, int output_size)
75{
76 time_t now = time(NULL);
77 struct tm *tm_info;
78 time_t duration;
79
80 duration = difftime(now, start_time);
81 tm_info = gmtime(&duration);
82
83 snprintf(output, output_size, "%3d %02d:%02d:%02d",
84 tm_info->tm_yday,
85 tm_info->tm_hour,
86 tm_info->tm_min,
87 tm_info->tm_sec);
88}
89
90/*
91 * parse_cpu_list - parse a cpu_list filling a char vector with cpus set
92 *
93 * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set the char
94 * in the monitored_cpus.
95 *
96 * XXX: convert to a bitmask.
97 */
98int parse_cpu_list(char *cpu_list, char **monitored_cpus)
99{
100 char *mon_cpus;
101 const char *p;
102 int end_cpu;
103 int nr_cpus;
104 int cpu;
105 int i;
106
107 nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
108
109 mon_cpus = malloc(nr_cpus * sizeof(char));
110 memset(mon_cpus, 0, (nr_cpus * sizeof(char)));
111
112 for (p = cpu_list; *p; ) {
113 cpu = atoi(p);
114 if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
115 goto err;
116
117 while (isdigit(*p))
118 p++;
119 if (*p == '-') {
120 p++;
121 end_cpu = atoi(p);
122 if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
123 goto err;
124 while (isdigit(*p))
125 p++;
126 } else
127 end_cpu = cpu;
128
129 if (cpu == end_cpu) {
130 debug_msg("cpu_list: adding cpu %d\n", cpu);
131 mon_cpus[cpu] = 1;
132 } else {
133 for (i = cpu; i <= end_cpu; i++) {
134 debug_msg("cpu_list: adding cpu %d\n", i);
135 mon_cpus[i] = 1;
136 }
137 }
138
139 if (*p == ',')
140 p++;
141 }
142
143 *monitored_cpus = mon_cpus;
144
145 return 0;
146
147err:
148 debug_msg("Error parsing the cpu list %s", cpu_list);
149 return 1;
150}
151
152/*
153 * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
154 */
155long parse_seconds_duration(char *val)
156{
157 char *end;
158 long t;
159
160 t = strtol(val, &end, 10);
161
162 if (end) {
163 switch (*end) {
164 case 's':
165 case 'S':
166 break;
167 case 'm':
168 case 'M':
169 t *= 60;
170 break;
171 case 'h':
172 case 'H':
173 t *= 60 * 60;
174 break;
175
176 case 'd':
177 case 'D':
178 t *= 24 * 60 * 60;
179 break;
180 }
181 }
182
183 return t;
184}
185
186/*
187 * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
188 */
189long parse_ns_duration(char *val)
190{
191 char *end;
192 long t;
193
194 t = strtol(val, &end, 10);
195
196 if (end) {
197 if (!strncmp(end, "ns", 2)) {
198 return t;
199 } else if (!strncmp(end, "us", 2)) {
200 t *= 1000;
201 return t;
202 } else if (!strncmp(end, "ms", 2)) {
203 t *= 1000 * 1000;
204 return t;
205 } else if (!strncmp(end, "s", 1)) {
206 t *= 1000 * 1000 * 1000;
207 return t;
208 }
209 return -1;
210 }
211
212 return t;
213}
214
215/*
216 * This is a set of helper functions to use SCHED_DEADLINE.
217 */
218#ifdef __x86_64__
219# define __NR_sched_setattr 314
220# define __NR_sched_getattr 315
221#elif __i386__
222# define __NR_sched_setattr 351
223# define __NR_sched_getattr 352
224#elif __arm__
225# define __NR_sched_setattr 380
226# define __NR_sched_getattr 381
227#elif __aarch64__
228# define __NR_sched_setattr 274
229# define __NR_sched_getattr 275
230#elif __powerpc__
231# define __NR_sched_setattr 355
232# define __NR_sched_getattr 356
233#elif __s390x__
234# define __NR_sched_setattr 345
235# define __NR_sched_getattr 346
236#endif
237
238#define SCHED_DEADLINE 6
239
240static inline int sched_setattr(pid_t pid, const struct sched_attr *attr,
241 unsigned int flags) {
242 return syscall(__NR_sched_setattr, pid, attr, flags);
243}
244
245static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
246 unsigned int size, unsigned int flags)
247{
248 return syscall(__NR_sched_getattr, pid, attr, size, flags);
249}
250
251int __set_sched_attr(int pid, struct sched_attr *attr)
252{
253 int flags = 0;
254 int retval;
255
256 retval = sched_setattr(pid, attr, flags);
257 if (retval < 0) {
258 err_msg("boost_with_deadline failed to boost pid %d: %s\n",
259 pid, strerror(errno));
260 return 1;
261 }
262
263 return 0;
264}
265/*
266 * set_comm_sched_attr - set sched params to threads starting with char *comm
267 *
268 * This function uses procps to list the currently running threads and then
269 * set the sched_attr *attr to the threads that start with char *comm. It is
270 * mainly used to set the priority to the kernel threads created by the
271 * tracers.
272 */
273int set_comm_sched_attr(const char *comm, struct sched_attr *attr)
274{
275 int flags = PROC_FILLCOM | PROC_FILLSTAT;
276 PROCTAB *ptp;
277 proc_t task;
278 int retval;
279
280 ptp = openproc(flags);
281 if (!ptp) {
282 err_msg("error openproc()\n");
283 return -ENOENT;
284 }
285
286 memset(&task, 0, sizeof(task));
287
288 while (readproc(ptp, &task)) {
289 retval = strncmp(comm, task.cmd, strlen(comm));
290 if (retval)
291 continue;
292 retval = __set_sched_attr(task.tid, attr);
293 if (retval)
294 goto out_err;
295 }
296
297 closeproc(ptp);
298 return 0;
299
300out_err:
301 closeproc(ptp);
302 return 1;
303}
304
305#define INVALID_VAL (~0L)
306static long get_long_ns_after_colon(char *start)
307{
308 long val = INVALID_VAL;
309
310 /* find the ":" */
311 start = strstr(start, ":");
312 if (!start)
313 return -1;
314
315 /* skip ":" */
316 start++;
317 val = parse_ns_duration(start);
318
319 return val;
320}
321
322static long get_long_after_colon(char *start)
323{
324 long val = INVALID_VAL;
325
326 /* find the ":" */
327 start = strstr(start, ":");
328 if (!start)
329 return -1;
330
331 /* skip ":" */
332 start++;
333 val = get_llong_from_str(start);
334
335 return val;
336}
337
338/*
339 * parse priority in the format:
340 * SCHED_OTHER:
341 * o:<prio>
342 * O:<prio>
343 * SCHED_RR:
344 * r:<prio>
345 * R:<prio>
346 * SCHED_FIFO:
347 * f:<prio>
348 * F:<prio>
349 * SCHED_DEADLINE:
350 * d:runtime:period
351 * D:runtime:period
352 */
353int parse_prio(char *arg, struct sched_attr *sched_param)
354{
355 long prio;
356 long runtime;
357 long period;
358
359 memset(sched_param, 0, sizeof(*sched_param));
360 sched_param->size = sizeof(*sched_param);
361
362 switch (arg[0]) {
363 case 'd':
364 case 'D':
365 /* d:runtime:period */
366 if (strlen(arg) < 4)
367 return -1;
368
369 runtime = get_long_ns_after_colon(arg);
370 if (runtime == INVALID_VAL)
371 return -1;
372
373 period = get_long_ns_after_colon(&arg[2]);
374 if (period == INVALID_VAL)
375 return -1;
376
377 if (runtime > period)
378 return -1;
379
380 sched_param->sched_policy = SCHED_DEADLINE;
381 sched_param->sched_runtime = runtime;
382 sched_param->sched_deadline = period;
383 sched_param->sched_period = period;
384 break;
385 case 'f':
386 case 'F':
387 /* f:prio */
388 prio = get_long_after_colon(arg);
389 if (prio == INVALID_VAL)
390 return -1;
391
392 if (prio < sched_get_priority_min(SCHED_FIFO))
393 return -1;
394 if (prio > sched_get_priority_max(SCHED_FIFO))
395 return -1;
396
397 sched_param->sched_policy = SCHED_FIFO;
398 sched_param->sched_priority = prio;
399 break;
400 case 'r':
401 case 'R':
402 /* r:prio */
403 prio = get_long_after_colon(arg);
404 if (prio == INVALID_VAL)
405 return -1;
406
407 if (prio < sched_get_priority_min(SCHED_RR))
408 return -1;
409 if (prio > sched_get_priority_max(SCHED_RR))
410 return -1;
411
412 sched_param->sched_policy = SCHED_RR;
413 sched_param->sched_priority = prio;
414 break;
415 case 'o':
416 case 'O':
417 /* o:prio */
418 prio = get_long_after_colon(arg);
419 if (prio == INVALID_VAL)
420 return -1;
421
422 if (prio < sched_get_priority_min(SCHED_OTHER))
423 return -1;
424 if (prio > sched_get_priority_max(SCHED_OTHER))
425 return -1;
426
427 sched_param->sched_policy = SCHED_OTHER;
428 sched_param->sched_priority = prio;
429 break;
430 default:
431 return -1;
432 }
433 return 0;
434}
435
436/*
437 * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
438 *
439 * This is used to reduce the exit from idle latency. The value
440 * will be reset once the file descriptor of /dev/cpu_dma_latecy
441 * is closed.
442 *
443 * Return: the /dev/cpu_dma_latecy file descriptor
444 */
445int set_cpu_dma_latency(int32_t latency)
446{
447 int retval;
448 int fd;
449
450 fd = open("/dev/cpu_dma_latency", O_RDWR);
451 if (fd < 0) {
452 err_msg("Error opening /dev/cpu_dma_latency\n");
453 return -1;
454 }
455
456 retval = write(fd, &latency, 4);
457 if (retval < 1) {
458 err_msg("Error setting /dev/cpu_dma_latency\n");
459 close(fd);
460 return -1;
461 }
462
463 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);
464
465 return fd;
466}