| 1 |
/* |
| 2 |
schedtool |
| 3 |
Copyright (C) 2002-2006 by Freek |
| 4 |
Please contact me via freshmeat.net |
| 5 |
Release under GPL, version 2 |
| 6 |
Use at your own risk. |
| 7 |
Inspired by setbatch (C) 2002 Ingo Molnar |
| 8 |
|
| 9 |
01/2006: |
| 10 |
included support for SCHED_IDLEPRIO for ck kernels |
| 11 |
|
| 12 |
01/2004: |
| 13 |
included SCHED_ISO patch by Con Kolivas |
| 14 |
|
| 15 |
11/2004: |
| 16 |
add probing for some features (priority) |
| 17 |
|
| 18 |
09/2008: |
| 19 |
change affinity calls to new cpu_set_t API |
| 20 |
|
| 21 |
|
| 22 |
Born in the need of querying and setting SCHED_* policies. |
| 23 |
All output, even errors, go to STDOUT to ease piping. |
| 24 |
|
| 25 |
|
| 26 |
Content: |
| 27 |
|
| 28 |
main code |
| 29 |
cmd-line parsing |
| 30 |
the engine |
| 31 |
set_/print_process |
| 32 |
usage |
| 33 |
|
| 34 |
*/ |
| 35 |
|
| 36 |
#define _GNU_SOURCE |
| 37 |
|
| 38 |
#include <stdio.h> |
| 39 |
#include <stdlib.h> |
| 40 |
#include <string.h> |
| 41 |
#include <ctype.h> |
| 42 |
#include <sys/types.h> |
| 43 |
#include <sys/time.h> |
| 44 |
#include <sys/resource.h> |
| 45 |
#include <sched.h> |
| 46 |
#include <unistd.h> |
| 47 |
#include <stdint.h> |
| 48 |
#include <math.h> |
| 49 |
|
| 50 |
#include "syscall_magic.h" |
| 51 |
#include "error.h" |
| 52 |
#include "util.h" |
| 53 |
|
| 54 |
|
| 55 |
/* various operation modes: print/set/affinity/fork */ |
| 56 |
#define MODE_NOTHING 0x0 |
| 57 |
#define MODE_PRINT 0x1 |
| 58 |
#define MODE_SETPOLICY 0x2 |
| 59 |
#define MODE_AFFINITY 0x4 |
| 60 |
#define MODE_EXEC 0x8 |
| 61 |
#define MODE_NICE 0x10 |
| 62 |
#define VERSION "1.3.0" |
| 63 |
|
| 64 |
/* |
| 65 |
constants are from the O(1)-sched kernel's include/sched.h |
| 66 |
I don't want to include kernel-headers. |
| 67 |
Included those defines for improved readability. |
| 68 |
*/ |
| 69 |
#undef SCHED_NORMAL |
| 70 |
#undef SCHED_FIFO |
| 71 |
#undef SCHED_RR |
| 72 |
#undef SCHED_BATCH |
| 73 |
#define SCHED_NORMAL 0 |
| 74 |
#define SCHED_FIFO 1 |
| 75 |
#define SCHED_RR 2 |
| 76 |
#define SCHED_BATCH 3 |
| 77 |
#define SCHED_ISO 4 |
| 78 |
#define SCHED_IDLEPRIO 5 |
| 79 |
#define SCHED_DEADLINE 6 |
| 80 |
|
| 81 |
/* for loops */ |
| 82 |
#define SCHED_MIN SCHED_NORMAL |
| 83 |
#define SCHED_MAX SCHED_DEADLINE |
| 84 |
|
| 85 |
#define CHECK_RANGE_POLICY(p) (p <= SCHED_MAX && p >= SCHED_MIN) |
| 86 |
#define CHECK_RANGE_NICE(n) (n <= 20 && n >= -20) |
| 87 |
|
| 88 |
/* |
| 89 |
4 bits of CPU_SETSIZE will naturally reduce to 1 char, so |
| 90 |
we'd only need [CPU_SETSIZE / 4 + 1] |
| 91 |
to be sure leave a bit of room and only do CPU_SETSIZE / 2 |
| 92 |
*/ |
| 93 |
#define CPUSET_HEXSTRING(name) char name[CPU_SETSIZE / 2] |
| 94 |
|
| 95 |
char *TAB[] = { |
| 96 |
"N: SCHED_NORMAL", |
| 97 |
"F: SCHED_FIFO", |
| 98 |
"R: SCHED_RR", |
| 99 |
"B: SCHED_BATCH", |
| 100 |
"I: SCHED_ISO", |
| 101 |
"D: SCHED_IDLEPRIO", |
| 102 |
"E: SCHED_DEADLINE", |
| 103 |
0 |
| 104 |
}; |
| 105 |
|
| 106 |
/* call it engine_s in lack of a better name */ |
| 107 |
struct engine_s { |
| 108 |
|
| 109 |
int mode; |
| 110 |
int policy; |
| 111 |
int prio; |
| 112 |
int nice; |
| 113 |
|
| 114 |
long rtime; |
| 115 |
long dline; |
| 116 |
long priod; |
| 117 |
|
| 118 |
unsigned flags; |
| 119 |
cpu_set_t aff_mask; |
| 120 |
|
| 121 |
/* # of args when going in PID-mode */ |
| 122 |
int n; |
| 123 |
char **args; |
| 124 |
}; |
| 125 |
|
| 126 |
|
| 127 |
int engine(struct engine_s *e); |
| 128 |
int set_process(pid_t pid, int policy, struct sched_param_ex *p); |
| 129 |
static inline int val_to_char(int v); |
| 130 |
static char * cpuset_to_str(cpu_set_t *mask, char *str); |
| 131 |
static inline int char_to_val(int c); |
| 132 |
static int str_to_cpuset(cpu_set_t *mask, const char* str); |
| 133 |
struct timespec us_to_tspec(long us); |
| 134 |
long tspec_to_us(struct timespec *ts); |
| 135 |
int parse_time(long *rtime, long *dline, long *priod, char *arg); |
| 136 |
int parse_flags(unsigned *f, char *arg); |
| 137 |
int parse_affinity(cpu_set_t *, char *arg); |
| 138 |
int set_affinity(pid_t pid, cpu_set_t *mask); |
| 139 |
int set_niceness(pid_t pid, int nice); |
| 140 |
void probe_sched_features(); |
| 141 |
void get_prio_min_max(int policy, int *min, int *max); |
| 142 |
void print_prio_min_max(int policy); |
| 143 |
void print_process(pid_t pid); |
| 144 |
void usage(void); |
| 145 |
|
| 146 |
|
| 147 |
extern char *optarg; |
| 148 |
extern int optind, opterr, optopt; |
| 149 |
extern int errno; |
| 150 |
|
| 151 |
int main(int ac, char **dc) |
| 152 |
{ |
| 153 |
/* |
| 154 |
policy: -1, to indicate it was not set; |
| 155 |
nice: 10, as nice/renice have as default; |
| 156 |
prio: 0 per default |
| 157 |
mode: MODE_NOTHING, no options set |
| 158 |
*/ |
| 159 |
int policy=-1, nice=10, prio=0, mode=MODE_NOTHING; |
| 160 |
long rtime=0, dline=0, priod=0; |
| 161 |
unsigned flags = 0; |
| 162 |
/* |
| 163 |
aff_mask: zero it out |
| 164 |
*/ |
| 165 |
cpu_set_t aff_mask; |
| 166 |
CPU_ZERO(&aff_mask); |
| 167 |
|
| 168 |
/* for getopt() */ |
| 169 |
int c; |
| 170 |
|
| 171 |
if (ac < 2) { |
| 172 |
usage(); |
| 173 |
return(0); |
| 174 |
} |
| 175 |
|
| 176 |
while((c=getopt(ac, dc, "+NFRBIDE012345M:a:p:t:f:n:ervh")) != -1) { |
| 177 |
|
| 178 |
switch(c) { |
| 179 |
case '0': |
| 180 |
case 'N': |
| 181 |
policy=SCHED_NORMAL; |
| 182 |
mode |= MODE_SETPOLICY; |
| 183 |
break; |
| 184 |
case '1': |
| 185 |
case 'F': |
| 186 |
policy=SCHED_FIFO; |
| 187 |
mode |= MODE_SETPOLICY; |
| 188 |
break; |
| 189 |
case '2': |
| 190 |
case 'R': |
| 191 |
policy=SCHED_RR; |
| 192 |
mode |= MODE_SETPOLICY; |
| 193 |
break; |
| 194 |
case '3': |
| 195 |
case 'B': |
| 196 |
policy=SCHED_BATCH; |
| 197 |
mode |= MODE_SETPOLICY; |
| 198 |
break; |
| 199 |
case '4': |
| 200 |
case 'I': |
| 201 |
policy=SCHED_ISO; |
| 202 |
mode |= MODE_SETPOLICY; |
| 203 |
break; |
| 204 |
case '5': |
| 205 |
case 'D': |
| 206 |
policy=SCHED_IDLEPRIO; |
| 207 |
mode |= MODE_SETPOLICY; |
| 208 |
break; |
| 209 |
case '6': |
| 210 |
case 'E': |
| 211 |
policy=SCHED_DEADLINE; |
| 212 |
mode |= MODE_SETPOLICY; |
| 213 |
break; |
| 214 |
case 'M': |
| 215 |
/* manual setting */ |
| 216 |
policy=atoi(optarg); |
| 217 |
mode |= MODE_SETPOLICY; |
| 218 |
break; |
| 219 |
case 'a': |
| 220 |
mode |= MODE_AFFINITY; |
| 221 |
parse_affinity(&aff_mask, optarg); |
| 222 |
break; |
| 223 |
case 'n': |
| 224 |
mode |= MODE_NICE; |
| 225 |
nice=atoi(optarg); |
| 226 |
break; |
| 227 |
case 'e': |
| 228 |
mode |= MODE_EXEC; |
| 229 |
break; |
| 230 |
case 'p': |
| 231 |
prio=atoi(optarg); |
| 232 |
break; |
| 233 |
case 't': |
| 234 |
parse_time(&rtime, &dline, &priod, optarg); |
| 235 |
break; |
| 236 |
case 'f': |
| 237 |
parse_flags(&flags, optarg); |
| 238 |
break; |
| 239 |
case 'r': |
| 240 |
probe_sched_features(); |
| 241 |
break; |
| 242 |
case 'v': |
| 243 |
/* the user wants feedback for each process */ |
| 244 |
mode |= MODE_PRINT; |
| 245 |
break; |
| 246 |
case 'V': |
| 247 |
case 'h': |
| 248 |
usage(); |
| 249 |
return(0); |
| 250 |
default: |
| 251 |
//printf("ERROR: unknown switch\n"); |
| 252 |
usage(); |
| 253 |
return(1); |
| 254 |
} |
| 255 |
} |
| 256 |
|
| 257 |
/* |
| 258 |
DAMN FUCKING |
| 259 |
parameter checking |
| 260 |
ARGH! |
| 261 |
*/ |
| 262 |
/* for _BATCH and _NORMAL, prio is ignored and must be 0*/ |
| 263 |
if((policy==SCHED_NORMAL || policy==SCHED_BATCH) && prio) { |
| 264 |
decode_error("%s call may fail as static PRIO must be 0 or omitted", |
| 265 |
TAB[policy] |
| 266 |
); |
| 267 |
|
| 268 |
/* _FIFO and _RR MUST have prio set */ |
| 269 |
} else if((policy==SCHED_FIFO || policy==SCHED_RR || policy==SCHED_ISO)) { |
| 270 |
|
| 271 |
#define CHECK_RANGE_PRIO(p, p_low, p_high) (p <= (p_high) && p >= (p_low)) |
| 272 |
/* FIFO and RR - check min/max priority */ |
| 273 |
int prio_min, prio_max; |
| 274 |
|
| 275 |
get_prio_min_max(policy, &prio_min, &prio_max); |
| 276 |
//int prio_max=sched_get_priority_max(policy); |
| 277 |
//int prio_min=sched_get_priority_min(policy); |
| 278 |
|
| 279 |
if(! CHECK_RANGE_PRIO(prio, prio_min, prio_max)) { |
| 280 |
// this could be problematic on very special custom kernels |
| 281 |
if(prio == 0) { |
| 282 |
decode_error("missing priority; specify static priority via -p"); |
| 283 |
} else { |
| 284 |
decode_error("PRIO %d is out of range %d-%d for %s", |
| 285 |
prio, |
| 286 |
prio_min, |
| 287 |
prio_max, |
| 288 |
TAB[policy] |
| 289 |
); |
| 290 |
} |
| 291 |
/* treat as all calls have failed */ |
| 292 |
return(ac-optind); |
| 293 |
} |
| 294 |
#undef CHECK_RANGE_PRIO |
| 295 |
} else if (policy == SCHED_DEADLINE) { |
| 296 |
if (!dline || !rtime) { |
| 297 |
decode_error("timing parameters are required for policy %s", TAB[policy]); |
| 298 |
return(ac-optind); |
| 299 |
} |
| 300 |
} |
| 301 |
|
| 302 |
/* no mode -> do querying */ |
| 303 |
if(! mode) { |
| 304 |
mode |= MODE_PRINT; |
| 305 |
} |
| 306 |
|
| 307 |
if( mode_set(mode, MODE_EXEC) && ! ( mode_set(mode, MODE_SETPOLICY) |
| 308 |
|| mode_set(mode, MODE_AFFINITY) |
| 309 |
|| mode_set(mode, MODE_NICE) ) |
| 310 |
|
| 311 |
) { |
| 312 |
/* we have nothing to do */ |
| 313 |
decode_error("Option -e needs scheduling-parameters, not given - exiting"); |
| 314 |
return(-1); |
| 315 |
} |
| 316 |
|
| 317 |
if(! CHECK_RANGE_NICE(nice)) { |
| 318 |
decode_error("NICE %d is out of range -20 to 20", nice); |
| 319 |
return(-1); |
| 320 |
} |
| 321 |
#undef CHECK_RANGE_NICE |
| 322 |
|
| 323 |
/* and: ignition */ |
| 324 |
{ |
| 325 |
struct engine_s stuff; |
| 326 |
/* now fill struct */ |
| 327 |
stuff.mode=mode; |
| 328 |
stuff.policy=policy; |
| 329 |
stuff.prio=prio; |
| 330 |
stuff.nice=nice; |
| 331 |
|
| 332 |
stuff.rtime=rtime; |
| 333 |
stuff.dline=dline; |
| 334 |
stuff.priod=priod; |
| 335 |
|
| 336 |
stuff.flags=flags; |
| 337 |
stuff.aff_mask=aff_mask; |
| 338 |
|
| 339 |
/* we have this much real args/PIDs to process */ |
| 340 |
stuff.n=ac-optind; |
| 341 |
|
| 342 |
/* this is the first real arg */ |
| 343 |
stuff.args=dc+optind; |
| 344 |
|
| 345 |
/* now go on and do what we were told */ |
| 346 |
return(engine(&stuff)); |
| 347 |
} |
| 348 |
} |
| 349 |
|
| 350 |
|
| 351 |
int engine(struct engine_s *e) |
| 352 |
{ |
| 353 |
int ret=0; |
| 354 |
int i; |
| 355 |
|
| 356 |
#ifdef DEBUG |
| 357 |
do { |
| 358 |
CPUSET_HEXSTRING(tmpaff); |
| 359 |
printf("Dumping mode: 0x%x\n", e->mode); |
| 360 |
printf("Dumping affinity: 0x%s\n", cpuset_to_str(&(e->aff_mask), tmpaff)); |
| 361 |
printf("We have %d args to do\n", e->n); |
| 362 |
for(i=0;i < e->n; i++) { |
| 363 |
printf("Dump arg %d: %s\n", i, e->args[i]); |
| 364 |
} |
| 365 |
} while(0); |
| 366 |
#endif |
| 367 |
|
| 368 |
/* |
| 369 |
handle normal query/set operation: |
| 370 |
set/query all given PIDs |
| 371 |
*/ |
| 372 |
for(i=0; i < e->n; i++) { |
| 373 |
|
| 374 |
int pid, tmpret=0; |
| 375 |
cpu_set_t affi; |
| 376 |
|
| 377 |
CPU_ZERO(&affi); |
| 378 |
CPU_SET(0, &affi); |
| 379 |
|
| 380 |
/* if in MODE_EXEC skip check for PIDs */ |
| 381 |
if(mode_set(e->mode, MODE_EXEC)) { |
| 382 |
pid=getpid(); |
| 383 |
goto exec_mode_special; |
| 384 |
} |
| 385 |
|
| 386 |
if(! (isdigit( *(e->args[i])) ) ) { |
| 387 |
decode_error("Ignoring arg %s: is not a PID", e->args[i]); |
| 388 |
continue; |
| 389 |
} |
| 390 |
|
| 391 |
pid=atoi(e->args[i]); |
| 392 |
|
| 393 |
exec_mode_special: |
| 394 |
if(mode_set(e->mode, MODE_SETPOLICY)) { |
| 395 |
struct sched_param_ex p; |
| 396 |
|
| 397 |
p.sched_priority= e->prio; |
| 398 |
p.sched_runtime=us_to_tspec(e->rtime); |
| 399 |
p.sched_deadline=us_to_tspec(e->dline); |
| 400 |
p.sched_period=us_to_tspec(e->priod); |
| 401 |
p.sched_flags=e->flags; |
| 402 |
|
| 403 |
/* |
| 404 |
accumulate possible errors |
| 405 |
the return value of main will indicate |
| 406 |
how much set-calls went wrong |
| 407 |
set_process returns -1 upon failure |
| 408 |
*/ |
| 409 |
tmpret=set_process(pid,e->policy,&p); |
| 410 |
ret += tmpret; |
| 411 |
|
| 412 |
/* don't proceed as something went wrong already */ |
| 413 |
if(tmpret) { |
| 414 |
continue; |
| 415 |
} |
| 416 |
|
| 417 |
} |
| 418 |
|
| 419 |
if(mode_set(e->mode, MODE_NICE)) { |
| 420 |
tmpret=set_niceness(pid, e->nice); |
| 421 |
ret += tmpret; |
| 422 |
|
| 423 |
if(tmpret) { |
| 424 |
continue; |
| 425 |
} |
| 426 |
|
| 427 |
} |
| 428 |
|
| 429 |
if(mode_set(e->mode, MODE_AFFINITY)) { |
| 430 |
tmpret=set_affinity(pid, &(e->aff_mask)); |
| 431 |
ret += tmpret; |
| 432 |
|
| 433 |
if(tmpret) { |
| 434 |
continue; |
| 435 |
} |
| 436 |
|
| 437 |
} |
| 438 |
|
| 439 |
/* and print process info when set, too */ |
| 440 |
if(mode_set(e->mode, MODE_PRINT)) { |
| 441 |
print_process(pid); |
| 442 |
} |
| 443 |
|
| 444 |
|
| 445 |
/* EXECUTE: at the end */ |
| 446 |
if(mode_set(e->mode, MODE_EXEC)) { |
| 447 |
|
| 448 |
char **new_argv=e->args; |
| 449 |
|
| 450 |
ret=execvp(*new_argv, new_argv); |
| 451 |
|
| 452 |
/* only reached on error */ |
| 453 |
decode_error("schedtool: Could not exec %s", *new_argv); |
| 454 |
return(ret); |
| 455 |
} |
| 456 |
} |
| 457 |
/* |
| 458 |
indicate how many errors we got; as ret is accumulated negative, |
| 459 |
convert to positive |
| 460 |
*/ |
| 461 |
return(abs(ret)); |
| 462 |
} |
| 463 |
|
| 464 |
|
| 465 |
int set_process(pid_t pid, int policy, struct sched_param_ex *p) |
| 466 |
{ |
| 467 |
int ret; |
| 468 |
|
| 469 |
char *msg1="could not set PID %d to %s"; |
| 470 |
char *msg2="could not set PID %d to raw policy #%d"; |
| 471 |
|
| 472 |
/* anything other than 0 indicates error */ |
| 473 |
if((ret=sched_setscheduler_ex(pid, policy, sizeof(*p), p))) { |
| 474 |
|
| 475 |
/* la la pointer mismatch .. lala */ |
| 476 |
decode_error((CHECK_RANGE_POLICY(policy) ? msg1 : msg2), |
| 477 |
pid, |
| 478 |
(CHECK_RANGE_POLICY(policy) ? TAB[policy] : policy) |
| 479 |
); |
| 480 |
return(ret); |
| 481 |
} |
| 482 |
return(0); |
| 483 |
} |
| 484 |
|
| 485 |
struct timespec us_to_tspec(long us) |
| 486 |
{ |
| 487 |
struct timespec ts; |
| 488 |
|
| 489 |
ts.tv_sec = us / 1000000; |
| 490 |
ts.tv_nsec = (us % 1000000) * 1000; |
| 491 |
|
| 492 |
return ts; |
| 493 |
} |
| 494 |
|
| 495 |
long tspec_to_us(struct timespec *ts) |
| 496 |
{ |
| 497 |
return round((ts->tv_sec * 1E9 + ts->tv_nsec) / 1000.0); |
| 498 |
} |
| 499 |
|
| 500 |
int parse_time(long *rtime, long *dline, long *priod, char *arg) |
| 501 |
{ |
| 502 |
char *str; |
| 503 |
|
| 504 |
str=strtok(arg, ":"); |
| 505 |
*rtime=strtol(str, NULL, 10); |
| 506 |
|
| 507 |
str=strtok(NULL, ":"); |
| 508 |
*dline=strtol(str, NULL, 10); |
| 509 |
|
| 510 |
str=strtok(NULL, ":"); |
| 511 |
if (str) |
| 512 |
*priod=strtol(str, NULL, 10); |
| 513 |
else |
| 514 |
*priod=*dline; |
| 515 |
|
| 516 |
#ifdef DEBUG |
| 517 |
printf("tmp_arg: %s -> rtime: %ld dline: %ld priod: %ld\n", arg, *rtime, *dline); |
| 518 |
#endif |
| 519 |
|
| 520 |
return str == NULL; |
| 521 |
} |
| 522 |
|
| 523 |
int parse_flags(unsigned *f, char *arg) |
| 524 |
{ |
| 525 |
*f = 0; |
| 526 |
if (strstr(arg, "s")) |
| 527 |
*f |= SF_SIG_RORUN; |
| 528 |
if (strstr(arg, "S")) |
| 529 |
*f |= SF_SIG_DMISS; |
| 530 |
if (strstr(arg, "D")) |
| 531 |
*f |= SF_BWRECL_DL; |
| 532 |
if (strstr(arg, "R")) |
| 533 |
*f |= SF_BWRECL_RT; |
| 534 |
if (strstr(arg, "O")) |
| 535 |
*f |= SF_BWRECL_NR; |
| 536 |
|
| 537 |
#ifdef DEBUG |
| 538 |
printf("tmp_arg: %s -> flags: %x\n", arg, *f); |
| 539 |
#endif |
| 540 |
|
| 541 |
return *f; |
| 542 |
} |
| 543 |
|
| 544 |
/* |
| 545 |
the following functions have been taken from taskset of util-linux |
| 546 |
(C) 2004 by Robert Love |
| 547 |
*/ |
| 548 |
static inline int val_to_char(int v) |
| 549 |
{ |
| 550 |
if (v >= 0 && v < 10) |
| 551 |
return '0' + v; |
| 552 |
else if (v >= 10 && v < 16) |
| 553 |
return ('a' - 10) + v; |
| 554 |
else |
| 555 |
return -1; |
| 556 |
} |
| 557 |
|
| 558 |
|
| 559 |
/* |
| 560 |
str seems to need to hold [CPU_SETSIZE * 7] chars, as in used in taskset |
| 561 |
however, 4 bits of CPU_SETSIZE will naturally reduce to 1 char |
| 562 |
(bits "1111" -> char "f") so CPU_SETSIZE / 4 + 1 should be sufficient |
| 563 |
|
| 564 |
will pad with zeroes to the start, so print the string starting at the |
| 565 |
returned char * |
| 566 |
*/ |
| 567 |
static char * cpuset_to_str(cpu_set_t *mask, char *str) |
| 568 |
{ |
| 569 |
int base; |
| 570 |
char *ptr = str; |
| 571 |
char *ret = 0; |
| 572 |
|
| 573 |
for (base = CPU_SETSIZE - 4; base >= 0; base -= 4) { |
| 574 |
char val = 0; |
| 575 |
if (CPU_ISSET(base, mask)) |
| 576 |
val |= 1; |
| 577 |
if (CPU_ISSET(base + 1, mask)) |
| 578 |
val |= 2; |
| 579 |
if (CPU_ISSET(base + 2, mask)) |
| 580 |
val |= 4; |
| 581 |
if (CPU_ISSET(base + 3, mask)) |
| 582 |
val |= 8; |
| 583 |
if (!ret && val) |
| 584 |
ret = ptr; |
| 585 |
*ptr++ = val_to_char(val); |
| 586 |
} |
| 587 |
*ptr = 0; |
| 588 |
return ret ? ret : ptr - 1; |
| 589 |
} |
| 590 |
|
| 591 |
|
| 592 |
static inline int char_to_val(int c) |
| 593 |
{ |
| 594 |
int cl; |
| 595 |
|
| 596 |
cl = tolower(c); |
| 597 |
if (c >= '0' && c <= '9') |
| 598 |
return c - '0'; |
| 599 |
else if (cl >= 'a' && cl <= 'f') |
| 600 |
return cl + (10 - 'a'); |
| 601 |
else |
| 602 |
return -1; |
| 603 |
} |
| 604 |
|
| 605 |
|
| 606 |
static int str_to_cpuset(cpu_set_t *mask, const char* str) |
| 607 |
{ |
| 608 |
int len = strlen(str); |
| 609 |
const char *ptr = str + len - 1; |
| 610 |
int base = 0; |
| 611 |
|
| 612 |
/* skip 0x, it's all hex anyway */ |
| 613 |
if(len > 1 && str[0] == '0' && str[1] == 'x') { |
| 614 |
str += 2; |
| 615 |
} |
| 616 |
|
| 617 |
CPU_ZERO(mask); |
| 618 |
while (ptr >= str) { |
| 619 |
char val = char_to_val(*ptr); |
| 620 |
if (val == (char) -1) |
| 621 |
return -1; |
| 622 |
if (val & 1) |
| 623 |
CPU_SET(base, mask); |
| 624 |
if (val & 2) |
| 625 |
CPU_SET(base + 1, mask); |
| 626 |
if (val & 4) |
| 627 |
CPU_SET(base + 2, mask); |
| 628 |
if (val & 8) |
| 629 |
CPU_SET(base + 3, mask); |
| 630 |
len--; |
| 631 |
ptr--; |
| 632 |
base += 4; |
| 633 |
} |
| 634 |
|
| 635 |
return 0; |
| 636 |
} |
| 637 |
/* end of functions taken */ |
| 638 |
|
| 639 |
|
| 640 |
/* mhm - we need something clever for all that CPU_SET() and CPU_ISSET() stuff */ |
| 641 |
int parse_affinity(cpu_set_t *mask, char *arg) |
| 642 |
{ |
| 643 |
cpu_set_t tmp_aff; |
| 644 |
char *tmp_arg; |
| 645 |
size_t valid_len; |
| 646 |
|
| 647 |
CPU_ZERO(&tmp_aff); |
| 648 |
|
| 649 |
if(*arg == '0' && *(arg+1) == 'x') { |
| 650 |
/* we're in standard hex mode */ |
| 651 |
str_to_cpuset(&tmp_aff, arg); |
| 652 |
|
| 653 |
} else if( (valid_len=strspn(arg, "0123456789,.")) ) { |
| 654 |
/* new list mode: schedtool -a 0,2 -> run on CPU0 and CPU2 */ |
| 655 |
|
| 656 |
/* split on ',' and '.', because '.' is near ',' :) */ |
| 657 |
while((tmp_arg=strsep(&arg, ",."))) { |
| 658 |
int tmp_cpu; |
| 659 |
|
| 660 |
if(isdigit((int)*tmp_arg)) { |
| 661 |
tmp_cpu=atoi(tmp_arg); |
| 662 |
CPU_SET(tmp_cpu, &tmp_aff); |
| 663 |
#ifdef DEBUG |
| 664 |
printf("tmp_arg: %s -> tmp_cpu: %d\n", tmp_arg, tmp_cpu); |
| 665 |
#endif |
| 666 |
} |
| 667 |
} |
| 668 |
|
| 669 |
} else { |
| 670 |
decode_error("affinity %s is not parseable", arg); |
| 671 |
exit(1); |
| 672 |
} |
| 673 |
|
| 674 |
*mask=tmp_aff; |
| 675 |
return 0; |
| 676 |
} |
| 677 |
|
| 678 |
|
| 679 |
int set_affinity(pid_t pid, cpu_set_t *mask) |
| 680 |
{ |
| 681 |
int ret; |
| 682 |
CPUSET_HEXSTRING(aff_hex); |
| 683 |
|
| 684 |
if((ret=sched_setaffinity(pid, sizeof(cpu_set_t), mask)) == -1) { |
| 685 |
decode_error("could not set PID %d to affinity 0x%s", |
| 686 |
pid, |
| 687 |
cpuset_to_str(mask, aff_hex) |
| 688 |
); |
| 689 |
return(ret); |
| 690 |
} |
| 691 |
return(0); |
| 692 |
} |
| 693 |
|
| 694 |
|
| 695 |
int set_niceness(pid_t pid, int nice) |
| 696 |
{ |
| 697 |
int ret; |
| 698 |
|
| 699 |
if((ret=setpriority(PRIO_PROCESS, pid, nice))) { |
| 700 |
decode_error("could not set PID %d to nice %d", |
| 701 |
pid, |
| 702 |
nice |
| 703 |
); |
| 704 |
return(ret); |
| 705 |
} |
| 706 |
return(0); |
| 707 |
} |
| 708 |
|
| 709 |
|
| 710 |
/* |
| 711 |
probe some features; just basic right now |
| 712 |
*/ |
| 713 |
void probe_sched_features() |
| 714 |
{ |
| 715 |
int i; |
| 716 |
|
| 717 |
for(i=SCHED_MIN; i <= SCHED_MAX; i++) { |
| 718 |
print_prio_min_max(i); |
| 719 |
} |
| 720 |
} |
| 721 |
|
| 722 |
|
| 723 |
/* |
| 724 |
get the min/max static priorites of a given policy. Max only work |
| 725 |
for SCHED_FIFO / SCHED_RR |
| 726 |
*/ |
| 727 |
void get_prio_min_max(int policy, int *min, int *max) |
| 728 |
{ |
| 729 |
*min=sched_get_priority_min(policy); |
| 730 |
*max=sched_get_priority_max(policy); |
| 731 |
} |
| 732 |
|
| 733 |
|
| 734 |
/* print the min and max priority of a given policy, like chrt does */ |
| 735 |
void print_prio_min_max(int policy) |
| 736 |
{ |
| 737 |
int min, max; |
| 738 |
|
| 739 |
get_prio_min_max(policy, &min, &max); |
| 740 |
|
| 741 |
switch(min|max) { |
| 742 |
|
| 743 |
case -1: |
| 744 |
printf("%-17s: policy not implemented\n", TAB[policy]); |
| 745 |
break; |
| 746 |
default: |
| 747 |
printf("%-17s: prio_min %d, prio_max %d\n", TAB[policy], min, max); |
| 748 |
break; |
| 749 |
} |
| 750 |
} |
| 751 |
|
| 752 |
|
| 753 |
/* |
| 754 |
Be more careful with at least the affinity call; someone may use an |
| 755 |
affinity-compiled version on a non-affinity kernel. |
| 756 |
This is getting more and more fu-gly. |
| 757 |
*/ |
| 758 |
void print_process(pid_t pid) |
| 759 |
{ |
| 760 |
int policy, nice; |
| 761 |
struct sched_param_ex p; |
| 762 |
cpu_set_t aff_mask; |
| 763 |
CPUSET_HEXSTRING(aff_mask_hex); |
| 764 |
|
| 765 |
|
| 766 |
CPU_ZERO(&aff_mask); |
| 767 |
|
| 768 |
/* strict error checking not needed - it works or not. */ |
| 769 |
errno=0; |
| 770 |
if( ((policy=sched_getscheduler(pid)) < 0) |
| 771 |
|| (sched_getparam_ex(pid, sizeof(p), &p) < 0) |
| 772 |
/* getpriority may successfully return negative values, so errno needs to be checked */ |
| 773 |
|| ((nice=getpriority(PRIO_PROCESS, pid)) && errno) |
| 774 |
) { |
| 775 |
decode_error("could not get scheduling-information for PID %d", pid); |
| 776 |
|
| 777 |
} else { |
| 778 |
|
| 779 |
/* do custom output for unknown policy */ |
| 780 |
if(! CHECK_RANGE_POLICY(policy)) { |
| 781 |
printf("PID %5d: PRIO %3d, POLICY %-5d <UNKNOWN>, NICE %3d", |
| 782 |
pid, |
| 783 |
p.sched_priority, |
| 784 |
policy, |
| 785 |
nice |
| 786 |
); |
| 787 |
} else { |
| 788 |
|
| 789 |
printf("PID %5d: PRIO %3d, POLICY %-17s, NICE %3d", |
| 790 |
pid, |
| 791 |
p.sched_priority, |
| 792 |
TAB[policy], |
| 793 |
nice |
| 794 |
); |
| 795 |
|
| 796 |
if (policy == SCHED_DEADLINE) { |
| 797 |
printf(", RUNTIME %Ldus DEADLINE %Ldus FLAGS 0x%04x" |
| 798 |
", CURR. RUNTIME %Ldus USED RUNTIME %Ldus", |
| 799 |
tspec_to_us(&p.sched_runtime), |
| 800 |
tspec_to_us(&p.sched_deadline), |
| 801 |
p.sched_flags, |
| 802 |
tspec_to_us(&p.curr_runtime), |
| 803 |
tspec_to_us(&p.used_runtime)); |
| 804 |
} |
| 805 |
} |
| 806 |
|
| 807 |
/* |
| 808 |
sched_getaffinity() seems to also return (int)4 on 2.6.8+ on x86 when successful. |
| 809 |
this goes against the documentation |
| 810 |
*/ |
| 811 |
if(sched_getaffinity(pid, sizeof(aff_mask), &aff_mask) == -1) { |
| 812 |
/* |
| 813 |
error or -ENOSYS |
| 814 |
simply ignore and reset errno! |
| 815 |
*/ |
| 816 |
errno=0; |
| 817 |
} else { |
| 818 |
printf(", AFFINITY 0x%s", cpuset_to_str(&aff_mask, aff_mask_hex)); |
| 819 |
} |
| 820 |
printf("\n"); |
| 821 |
} |
| 822 |
} |
| 823 |
|
| 824 |
|
| 825 |
void usage(void) |
| 826 |
{ |
| 827 |
printf( |
| 828 |
"get/set scheduling policies - v" VERSION ", GPL'd, NO WARRANTY\n" \ |
| 829 |
"USAGE: schedtool PIDS - query PIDS\n" \ |
| 830 |
" schedtool [OPTIONS] PIDS - set PIDS\n" \ |
| 831 |
" schedtool [OPTIONS] -e COMMAND - exec COMMAND\n" \ |
| 832 |
"\n" \ |
| 833 |
"set scheduling policies:\n" \ |
| 834 |
" -N for SCHED_NORMAL\n" \ |
| 835 |
" -F -p PRIO for SCHED_FIFO only as root\n" \ |
| 836 |
" -R -p PRIO for SCHED_RR only as root\n" \ |
| 837 |
" -B for SCHED_BATCH\n" \ |
| 838 |
" -I -p PRIO for SCHED_ISO\n" \ |
| 839 |
" -D for SCHED_IDLEPRIO\n" \ |
| 840 |
" -E -t rt:dl[:pr] for SCHED_DEADLINE only as root\n" \ |
| 841 |
" [ -f sSDRO ]\n" \ |
| 842 |
"\n" \ |
| 843 |
" -M POLICY for manual mode; raw number for POLICY\n" \ |
| 844 |
" -p STATIC_PRIORITY usually 1-99; only for FIFO or RR\n" \ |
| 845 |
" higher numbers means higher priority\n" \ |
| 846 |
" -n NICE_LEVEL set niceness to NICE_LEVEL\n" \ |
| 847 |
" -a AFFINITY_MASK set CPU-affinity to bitmask or list\n\n" \ |
| 848 |
" -e COMMAND [ARGS] start COMMAND with specified policy/priority\n" \ |
| 849 |
" -r display priority min/max for each policy\n" \ |
| 850 |
" -v be verbose\n" \ |
| 851 |
"\n" \ |
| 852 |
); |
| 853 |
/* printf("Parent "); |
| 854 |
print_process(getppid()); */ |
| 855 |
|
| 856 |
} |