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
}