1 // SPDX-License-Identifier: GPL-2.0
19 #define MAX_MSG_LENGTH 1024
23 * err_msg - print an error message to the stderr
25 void err_msg(const char *fmt, ...)
27 char message[MAX_MSG_LENGTH];
31 vsnprintf(message, sizeof(message), fmt, ap);
34 fprintf(stderr, "%s", message);
38 * debug_msg - print a debug message to stderr if debug is set
40 void debug_msg(const char *fmt, ...)
42 char message[MAX_MSG_LENGTH];
49 vsnprintf(message, sizeof(message), fmt, ap);
52 fprintf(stderr, "%s", message);
56 * get_llong_from_str - get a long long int from a string
58 long long get_llong_from_str(char *start)
64 value = strtoll(start, &end, 10);
65 if (errno || start == end)
72 * get_duration - fill output with a human readable duration since start_time
74 void get_duration(time_t start_time, char *output, int output_size)
76 time_t now = time(NULL);
80 duration = difftime(now, start_time);
81 tm_info = gmtime(&duration);
83 snprintf(output, output_size, "%3d %02d:%02d:%02d",
91 * parse_cpu_list - parse a cpu_list filling a char vector with cpus set
93 * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set the char
94 * in the monitored_cpus.
96 * XXX: convert to a bitmask.
98 int parse_cpu_list(char *cpu_list, char **monitored_cpus)
107 nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
109 mon_cpus = calloc(nr_cpus, sizeof(char));
113 for (p = cpu_list; *p; ) {
115 if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
123 if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
130 if (cpu == end_cpu) {
131 debug_msg("cpu_list: adding cpu %d\n", cpu);
134 for (i = cpu; i <= end_cpu; i++) {
135 debug_msg("cpu_list: adding cpu %d\n", i);
144 *monitored_cpus = mon_cpus;
149 debug_msg("Error parsing the cpu list %s", cpu_list);
154 * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
156 long parse_seconds_duration(char *val)
161 t = strtol(val, &end, 10);
188 * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
190 long parse_ns_duration(char *val)
195 t = strtol(val, &end, 10);
198 if (!strncmp(end, "ns", 2)) {
200 } else if (!strncmp(end, "us", 2)) {
203 } else if (!strncmp(end, "ms", 2)) {
206 } else if (!strncmp(end, "s", 1)) {
207 t *= 1000 * 1000 * 1000;
217 * This is a set of helper functions to use SCHED_DEADLINE.
220 # define __NR_sched_setattr 314
221 # define __NR_sched_getattr 315
223 # define __NR_sched_setattr 351
224 # define __NR_sched_getattr 352
226 # define __NR_sched_setattr 380
227 # define __NR_sched_getattr 381
228 #elif __aarch64__ || __riscv
229 # define __NR_sched_setattr 274
230 # define __NR_sched_getattr 275
232 # define __NR_sched_setattr 355
233 # define __NR_sched_getattr 356
235 # define __NR_sched_setattr 345
236 # define __NR_sched_getattr 346
239 #define SCHED_DEADLINE 6
241 static inline int sched_setattr(pid_t pid, const struct sched_attr *attr,
242 unsigned int flags) {
243 return syscall(__NR_sched_setattr, pid, attr, flags);
246 static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
247 unsigned int size, unsigned int flags)
249 return syscall(__NR_sched_getattr, pid, attr, size, flags);
252 int __set_sched_attr(int pid, struct sched_attr *attr)
257 retval = sched_setattr(pid, attr, flags);
259 err_msg("Failed to set sched attributes to the pid %d: %s\n",
260 pid, strerror(errno));
268 * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm
270 * Check if the procfs entry is a directory of a process, and then check if the
271 * process has a comm with the prefix set in char *comm_prefix. As the
272 * current users of this function only check for kernel threads, there is no
273 * need to check for the threads for the process.
275 * Return: True if the proc_entry contains a comm file with comm_prefix*.
276 * Otherwise returns false.
278 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry)
280 char buffer[MAX_PATH];
284 if (proc_entry->d_type != DT_DIR)
287 if (*proc_entry->d_name == '.')
290 /* check if the string is a pid */
291 for (t_name = proc_entry->d_name; t_name; t_name++) {
292 if (!isdigit(*t_name))
299 snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name);
300 comm_fd = open(buffer, O_RDONLY);
304 memset(buffer, 0, MAX_PATH);
305 retval = read(comm_fd, buffer, MAX_PATH);
312 retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
316 /* comm already have \n */
317 debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
323 * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix
325 * This function uses procfs to list the currently running threads and then set the
326 * sched_attr *attr to the threads that start with char *comm_prefix. It is
327 * mainly used to set the priority to the kernel threads created by the
330 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
332 struct dirent *proc_entry;
336 if (strlen(comm_prefix) >= MAX_PATH) {
337 err_msg("Command prefix is too long: %d < strlen(%s)\n",
338 MAX_PATH, comm_prefix);
342 procfs = opendir("/proc");
344 err_msg("Could not open procfs\n");
348 while ((proc_entry = readdir(procfs))) {
350 retval = procfs_is_workload_pid(comm_prefix, proc_entry);
354 /* procfs_is_workload_pid confirmed it is a pid */
355 retval = __set_sched_attr(atoi(proc_entry->d_name), attr);
357 err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
361 debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
370 #define INVALID_VAL (~0L)
371 static long get_long_ns_after_colon(char *start)
373 long val = INVALID_VAL;
376 start = strstr(start, ":");
382 val = parse_ns_duration(start);
387 static long get_long_after_colon(char *start)
389 long val = INVALID_VAL;
392 start = strstr(start, ":");
398 val = get_llong_from_str(start);
404 * parse priority in the format:
418 int parse_prio(char *arg, struct sched_attr *sched_param)
424 memset(sched_param, 0, sizeof(*sched_param));
425 sched_param->size = sizeof(*sched_param);
430 /* d:runtime:period */
434 runtime = get_long_ns_after_colon(arg);
435 if (runtime == INVALID_VAL)
438 period = get_long_ns_after_colon(&arg[2]);
439 if (period == INVALID_VAL)
442 if (runtime > period)
445 sched_param->sched_policy = SCHED_DEADLINE;
446 sched_param->sched_runtime = runtime;
447 sched_param->sched_deadline = period;
448 sched_param->sched_period = period;
453 prio = get_long_after_colon(arg);
454 if (prio == INVALID_VAL)
457 if (prio < sched_get_priority_min(SCHED_FIFO))
459 if (prio > sched_get_priority_max(SCHED_FIFO))
462 sched_param->sched_policy = SCHED_FIFO;
463 sched_param->sched_priority = prio;
468 prio = get_long_after_colon(arg);
469 if (prio == INVALID_VAL)
472 if (prio < sched_get_priority_min(SCHED_RR))
474 if (prio > sched_get_priority_max(SCHED_RR))
477 sched_param->sched_policy = SCHED_RR;
478 sched_param->sched_priority = prio;
483 prio = get_long_after_colon(arg);
484 if (prio == INVALID_VAL)
487 if (prio < sched_get_priority_min(SCHED_OTHER))
489 if (prio > sched_get_priority_max(SCHED_OTHER))
492 sched_param->sched_policy = SCHED_OTHER;
493 sched_param->sched_priority = prio;
502 * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
504 * This is used to reduce the exit from idle latency. The value
505 * will be reset once the file descriptor of /dev/cpu_dma_latecy
508 * Return: the /dev/cpu_dma_latecy file descriptor
510 int set_cpu_dma_latency(int32_t latency)
515 fd = open("/dev/cpu_dma_latency", O_RDWR);
517 err_msg("Error opening /dev/cpu_dma_latency\n");
521 retval = write(fd, &latency, 4);
523 err_msg("Error setting /dev/cpu_dma_latency\n");
528 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);