0001
0002
0003
0004
0005
0006 #include <dirent.h>
0007 #include <stdarg.h>
0008 #include <stdlib.h>
0009 #include <string.h>
0010 #include <unistd.h>
0011 #include <ctype.h>
0012 #include <errno.h>
0013 #include <fcntl.h>
0014 #include <sched.h>
0015 #include <stdio.h>
0016
0017 #include "utils.h"
0018
0019 #define MAX_MSG_LENGTH 1024
0020 int config_debug;
0021
0022
0023
0024
0025 void err_msg(const char *fmt, ...)
0026 {
0027 char message[MAX_MSG_LENGTH];
0028 va_list ap;
0029
0030 va_start(ap, fmt);
0031 vsnprintf(message, sizeof(message), fmt, ap);
0032 va_end(ap);
0033
0034 fprintf(stderr, "%s", message);
0035 }
0036
0037
0038
0039
0040 void debug_msg(const char *fmt, ...)
0041 {
0042 char message[MAX_MSG_LENGTH];
0043 va_list ap;
0044
0045 if (!config_debug)
0046 return;
0047
0048 va_start(ap, fmt);
0049 vsnprintf(message, sizeof(message), fmt, ap);
0050 va_end(ap);
0051
0052 fprintf(stderr, "%s", message);
0053 }
0054
0055
0056
0057
0058 long long get_llong_from_str(char *start)
0059 {
0060 long long value;
0061 char *end;
0062
0063 errno = 0;
0064 value = strtoll(start, &end, 10);
0065 if (errno || start == end)
0066 return -1;
0067
0068 return value;
0069 }
0070
0071
0072
0073
0074 void get_duration(time_t start_time, char *output, int output_size)
0075 {
0076 time_t now = time(NULL);
0077 struct tm *tm_info;
0078 time_t duration;
0079
0080 duration = difftime(now, start_time);
0081 tm_info = gmtime(&duration);
0082
0083 snprintf(output, output_size, "%3d %02d:%02d:%02d",
0084 tm_info->tm_yday,
0085 tm_info->tm_hour,
0086 tm_info->tm_min,
0087 tm_info->tm_sec);
0088 }
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098 int parse_cpu_list(char *cpu_list, char **monitored_cpus)
0099 {
0100 char *mon_cpus;
0101 const char *p;
0102 int end_cpu;
0103 int nr_cpus;
0104 int cpu;
0105 int i;
0106
0107 nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
0108
0109 mon_cpus = calloc(nr_cpus, sizeof(char));
0110 if (!mon_cpus)
0111 goto err;
0112
0113 for (p = cpu_list; *p; ) {
0114 cpu = atoi(p);
0115 if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
0116 goto err;
0117
0118 while (isdigit(*p))
0119 p++;
0120 if (*p == '-') {
0121 p++;
0122 end_cpu = atoi(p);
0123 if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
0124 goto err;
0125 while (isdigit(*p))
0126 p++;
0127 } else
0128 end_cpu = cpu;
0129
0130 if (cpu == end_cpu) {
0131 debug_msg("cpu_list: adding cpu %d\n", cpu);
0132 mon_cpus[cpu] = 1;
0133 } else {
0134 for (i = cpu; i <= end_cpu; i++) {
0135 debug_msg("cpu_list: adding cpu %d\n", i);
0136 mon_cpus[i] = 1;
0137 }
0138 }
0139
0140 if (*p == ',')
0141 p++;
0142 }
0143
0144 *monitored_cpus = mon_cpus;
0145
0146 return 0;
0147
0148 err:
0149 debug_msg("Error parsing the cpu list %s", cpu_list);
0150 return 1;
0151 }
0152
0153
0154
0155
0156 long parse_seconds_duration(char *val)
0157 {
0158 char *end;
0159 long t;
0160
0161 t = strtol(val, &end, 10);
0162
0163 if (end) {
0164 switch (*end) {
0165 case 's':
0166 case 'S':
0167 break;
0168 case 'm':
0169 case 'M':
0170 t *= 60;
0171 break;
0172 case 'h':
0173 case 'H':
0174 t *= 60 * 60;
0175 break;
0176
0177 case 'd':
0178 case 'D':
0179 t *= 24 * 60 * 60;
0180 break;
0181 }
0182 }
0183
0184 return t;
0185 }
0186
0187
0188
0189
0190 long parse_ns_duration(char *val)
0191 {
0192 char *end;
0193 long t;
0194
0195 t = strtol(val, &end, 10);
0196
0197 if (end) {
0198 if (!strncmp(end, "ns", 2)) {
0199 return t;
0200 } else if (!strncmp(end, "us", 2)) {
0201 t *= 1000;
0202 return t;
0203 } else if (!strncmp(end, "ms", 2)) {
0204 t *= 1000 * 1000;
0205 return t;
0206 } else if (!strncmp(end, "s", 1)) {
0207 t *= 1000 * 1000 * 1000;
0208 return t;
0209 }
0210 return -1;
0211 }
0212
0213 return t;
0214 }
0215
0216
0217
0218
0219 #ifdef __x86_64__
0220 # define __NR_sched_setattr 314
0221 # define __NR_sched_getattr 315
0222 #elif __i386__
0223 # define __NR_sched_setattr 351
0224 # define __NR_sched_getattr 352
0225 #elif __arm__
0226 # define __NR_sched_setattr 380
0227 # define __NR_sched_getattr 381
0228 #elif __aarch64__ || __riscv
0229 # define __NR_sched_setattr 274
0230 # define __NR_sched_getattr 275
0231 #elif __powerpc__
0232 # define __NR_sched_setattr 355
0233 # define __NR_sched_getattr 356
0234 #elif __s390x__
0235 # define __NR_sched_setattr 345
0236 # define __NR_sched_getattr 346
0237 #endif
0238
0239 #define SCHED_DEADLINE 6
0240
0241 static inline int sched_setattr(pid_t pid, const struct sched_attr *attr,
0242 unsigned int flags) {
0243 return syscall(__NR_sched_setattr, pid, attr, flags);
0244 }
0245
0246 static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
0247 unsigned int size, unsigned int flags)
0248 {
0249 return syscall(__NR_sched_getattr, pid, attr, size, flags);
0250 }
0251
0252 int __set_sched_attr(int pid, struct sched_attr *attr)
0253 {
0254 int flags = 0;
0255 int retval;
0256
0257 retval = sched_setattr(pid, attr, flags);
0258 if (retval < 0) {
0259 err_msg("Failed to set sched attributes to the pid %d: %s\n",
0260 pid, strerror(errno));
0261 return 1;
0262 }
0263
0264 return 0;
0265 }
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry)
0279 {
0280 char buffer[MAX_PATH];
0281 int comm_fd, retval;
0282 char *t_name;
0283
0284 if (proc_entry->d_type != DT_DIR)
0285 return 0;
0286
0287 if (*proc_entry->d_name == '.')
0288 return 0;
0289
0290
0291 for (t_name = proc_entry->d_name; t_name; t_name++) {
0292 if (!isdigit(*t_name))
0293 break;
0294 }
0295
0296 if (*t_name != '\0')
0297 return 0;
0298
0299 snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name);
0300 comm_fd = open(buffer, O_RDONLY);
0301 if (comm_fd < 0)
0302 return 0;
0303
0304 memset(buffer, 0, MAX_PATH);
0305 retval = read(comm_fd, buffer, MAX_PATH);
0306
0307 close(comm_fd);
0308
0309 if (retval <= 0)
0310 return 0;
0311
0312 retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
0313 if (retval)
0314 return 0;
0315
0316
0317 debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
0318
0319 return 1;
0320 }
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
0331 {
0332 struct dirent *proc_entry;
0333 DIR *procfs;
0334 int retval;
0335
0336 if (strlen(comm_prefix) >= MAX_PATH) {
0337 err_msg("Command prefix is too long: %d < strlen(%s)\n",
0338 MAX_PATH, comm_prefix);
0339 return 1;
0340 }
0341
0342 procfs = opendir("/proc");
0343 if (!procfs) {
0344 err_msg("Could not open procfs\n");
0345 return 1;
0346 }
0347
0348 while ((proc_entry = readdir(procfs))) {
0349
0350 retval = procfs_is_workload_pid(comm_prefix, proc_entry);
0351 if (!retval)
0352 continue;
0353
0354
0355 retval = __set_sched_attr(atoi(proc_entry->d_name), attr);
0356 if (retval) {
0357 err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
0358 goto out_err;
0359 }
0360
0361 debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
0362 }
0363 return 0;
0364
0365 out_err:
0366 closedir(procfs);
0367 return 1;
0368 }
0369
0370 #define INVALID_VAL (~0L)
0371 static long get_long_ns_after_colon(char *start)
0372 {
0373 long val = INVALID_VAL;
0374
0375
0376 start = strstr(start, ":");
0377 if (!start)
0378 return -1;
0379
0380
0381 start++;
0382 val = parse_ns_duration(start);
0383
0384 return val;
0385 }
0386
0387 static long get_long_after_colon(char *start)
0388 {
0389 long val = INVALID_VAL;
0390
0391
0392 start = strstr(start, ":");
0393 if (!start)
0394 return -1;
0395
0396
0397 start++;
0398 val = get_llong_from_str(start);
0399
0400 return val;
0401 }
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418 int parse_prio(char *arg, struct sched_attr *sched_param)
0419 {
0420 long prio;
0421 long runtime;
0422 long period;
0423
0424 memset(sched_param, 0, sizeof(*sched_param));
0425 sched_param->size = sizeof(*sched_param);
0426
0427 switch (arg[0]) {
0428 case 'd':
0429 case 'D':
0430
0431 if (strlen(arg) < 4)
0432 return -1;
0433
0434 runtime = get_long_ns_after_colon(arg);
0435 if (runtime == INVALID_VAL)
0436 return -1;
0437
0438 period = get_long_ns_after_colon(&arg[2]);
0439 if (period == INVALID_VAL)
0440 return -1;
0441
0442 if (runtime > period)
0443 return -1;
0444
0445 sched_param->sched_policy = SCHED_DEADLINE;
0446 sched_param->sched_runtime = runtime;
0447 sched_param->sched_deadline = period;
0448 sched_param->sched_period = period;
0449 break;
0450 case 'f':
0451 case 'F':
0452
0453 prio = get_long_after_colon(arg);
0454 if (prio == INVALID_VAL)
0455 return -1;
0456
0457 if (prio < sched_get_priority_min(SCHED_FIFO))
0458 return -1;
0459 if (prio > sched_get_priority_max(SCHED_FIFO))
0460 return -1;
0461
0462 sched_param->sched_policy = SCHED_FIFO;
0463 sched_param->sched_priority = prio;
0464 break;
0465 case 'r':
0466 case 'R':
0467
0468 prio = get_long_after_colon(arg);
0469 if (prio == INVALID_VAL)
0470 return -1;
0471
0472 if (prio < sched_get_priority_min(SCHED_RR))
0473 return -1;
0474 if (prio > sched_get_priority_max(SCHED_RR))
0475 return -1;
0476
0477 sched_param->sched_policy = SCHED_RR;
0478 sched_param->sched_priority = prio;
0479 break;
0480 case 'o':
0481 case 'O':
0482
0483 prio = get_long_after_colon(arg);
0484 if (prio == INVALID_VAL)
0485 return -1;
0486
0487 if (prio < sched_get_priority_min(SCHED_OTHER))
0488 return -1;
0489 if (prio > sched_get_priority_max(SCHED_OTHER))
0490 return -1;
0491
0492 sched_param->sched_policy = SCHED_OTHER;
0493 sched_param->sched_priority = prio;
0494 break;
0495 default:
0496 return -1;
0497 }
0498 return 0;
0499 }
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510 int set_cpu_dma_latency(int32_t latency)
0511 {
0512 int retval;
0513 int fd;
0514
0515 fd = open("/dev/cpu_dma_latency", O_RDWR);
0516 if (fd < 0) {
0517 err_msg("Error opening /dev/cpu_dma_latency\n");
0518 return -1;
0519 }
0520
0521 retval = write(fd, &latency, 4);
0522 if (retval < 1) {
0523 err_msg("Error setting /dev/cpu_dma_latency\n");
0524 close(fd);
0525 return -1;
0526 }
0527
0528 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);
0529
0530 return fd;
0531 }