1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
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 = malloc(nr_cpus * sizeof(char));
110 memset(mon_cpus, 0, (nr_cpus * sizeof(char)));
112 for (p = cpu_list; *p; ) {
114 if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
122 if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
129 if (cpu == end_cpu) {
130 debug_msg("cpu_list: adding cpu %d\n", cpu);
133 for (i = cpu; i <= end_cpu; i++) {
134 debug_msg("cpu_list: adding cpu %d\n", i);
143 *monitored_cpus = mon_cpus;
148 debug_msg("Error parsing the cpu list %s", cpu_list);
153 * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
155 long parse_seconds_duration(char *val)
160 t = strtol(val, &end, 10);
187 * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
189 long parse_ns_duration(char *val)
194 t = strtol(val, &end, 10);
197 if (!strncmp(end, "ns", 2)) {
199 } else if (!strncmp(end, "us", 2)) {
202 } else if (!strncmp(end, "ms", 2)) {
205 } else if (!strncmp(end, "s", 1)) {
206 t *= 1000 * 1000 * 1000;
216 * This is a set of helper functions to use SCHED_DEADLINE.
219 # define __NR_sched_setattr 314
220 # define __NR_sched_getattr 315
222 # define __NR_sched_setattr 351
223 # define __NR_sched_getattr 352
225 # define __NR_sched_setattr 380
226 # define __NR_sched_getattr 381
228 # define __NR_sched_setattr 274
229 # define __NR_sched_getattr 275
231 # define __NR_sched_setattr 355
232 # define __NR_sched_getattr 356
234 # define __NR_sched_setattr 345
235 # define __NR_sched_getattr 346
238 #define SCHED_DEADLINE 6
240 static 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);
245 static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
246 unsigned int size, unsigned int flags)
248 return syscall(__NR_sched_getattr, pid, attr, size, flags);
251 int __set_sched_attr(int pid, struct sched_attr *attr)
256 retval = sched_setattr(pid, attr, flags);
258 err_msg("Failed to set sched attributes to the pid %d: %s\n",
259 pid, strerror(errno));
267 * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm
269 * Check if the procfs entry is a directory of a process, and then check if the
270 * process has a comm with the prefix set in char *comm_prefix. As the
271 * current users of this function only check for kernel threads, there is no
272 * need to check for the threads for the process.
274 * Return: True if the proc_entry contains a comm file with comm_prefix*.
275 * Otherwise returns false.
277 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry)
279 char buffer[MAX_PATH];
283 if (proc_entry->d_type != DT_DIR)
286 if (*proc_entry->d_name == '.')
289 /* check if the string is a pid */
290 for (t_name = proc_entry->d_name; t_name; t_name++) {
291 if (!isdigit(*t_name))
298 snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name);
299 comm_fd = open(buffer, O_RDONLY);
303 memset(buffer, 0, MAX_PATH);
304 retval = read(comm_fd, buffer, MAX_PATH);
311 retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
315 /* comm already have \n */
316 debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
322 * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix
324 * This function uses procfs to list the currently running threads and then set the
325 * sched_attr *attr to the threads that start with char *comm_prefix. It is
326 * mainly used to set the priority to the kernel threads created by the
329 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
331 struct dirent *proc_entry;
335 if (strlen(comm_prefix) >= MAX_PATH) {
336 err_msg("Command prefix is too long: %d < strlen(%s)\n",
337 MAX_PATH, comm_prefix);
341 procfs = opendir("/proc");
343 err_msg("Could not open procfs\n");
347 while ((proc_entry = readdir(procfs))) {
349 retval = procfs_is_workload_pid(comm_prefix, proc_entry);
353 /* procfs_is_workload_pid confirmed it is a pid */
354 retval = __set_sched_attr(atoi(proc_entry->d_name), attr);
356 err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
360 debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
369 #define INVALID_VAL (~0L)
370 static long get_long_ns_after_colon(char *start)
372 long val = INVALID_VAL;
375 start = strstr(start, ":");
381 val = parse_ns_duration(start);
386 static long get_long_after_colon(char *start)
388 long val = INVALID_VAL;
391 start = strstr(start, ":");
397 val = get_llong_from_str(start);
403 * parse priority in the format:
417 int parse_prio(char *arg, struct sched_attr *sched_param)
423 memset(sched_param, 0, sizeof(*sched_param));
424 sched_param->size = sizeof(*sched_param);
429 /* d:runtime:period */
433 runtime = get_long_ns_after_colon(arg);
434 if (runtime == INVALID_VAL)
437 period = get_long_ns_after_colon(&arg[2]);
438 if (period == INVALID_VAL)
441 if (runtime > period)
444 sched_param->sched_policy = SCHED_DEADLINE;
445 sched_param->sched_runtime = runtime;
446 sched_param->sched_deadline = period;
447 sched_param->sched_period = period;
452 prio = get_long_after_colon(arg);
453 if (prio == INVALID_VAL)
456 if (prio < sched_get_priority_min(SCHED_FIFO))
458 if (prio > sched_get_priority_max(SCHED_FIFO))
461 sched_param->sched_policy = SCHED_FIFO;
462 sched_param->sched_priority = prio;
467 prio = get_long_after_colon(arg);
468 if (prio == INVALID_VAL)
471 if (prio < sched_get_priority_min(SCHED_RR))
473 if (prio > sched_get_priority_max(SCHED_RR))
476 sched_param->sched_policy = SCHED_RR;
477 sched_param->sched_priority = prio;
482 prio = get_long_after_colon(arg);
483 if (prio == INVALID_VAL)
486 if (prio < sched_get_priority_min(SCHED_OTHER))
488 if (prio > sched_get_priority_max(SCHED_OTHER))
491 sched_param->sched_policy = SCHED_OTHER;
492 sched_param->sched_priority = prio;
501 * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
503 * This is used to reduce the exit from idle latency. The value
504 * will be reset once the file descriptor of /dev/cpu_dma_latecy
507 * Return: the /dev/cpu_dma_latecy file descriptor
509 int set_cpu_dma_latency(int32_t latency)
514 fd = open("/dev/cpu_dma_latency", O_RDWR);
516 err_msg("Error opening /dev/cpu_dma_latency\n");
520 retval = write(fd, &latency, 4);
522 err_msg("Error setting /dev/cpu_dma_latency\n");
527 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);