Upload Tizen:Base source
[framework/base/util-linux-ng.git] / schedutils / taskset.c
1 /*
2  * taskset.c - taskset
3  * Command-line utility for setting and retrieving a task's CPU affinity
4  *
5  * Robert Love <rml@tech9.net>          25 April 2002
6  *
7  * Linux kernels as of 2.5.8 provide the needed syscalls for
8  * working with a task's cpu affinity.  Currently 2.4 does not
9  * support these syscalls, but patches are available at:
10  *
11  *      http://www.kernel.org/pub/linux/kernel/people/rml/cpu-affinity/
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License, v2, as
15  * published by the Free Software Foundation
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  * Copyright (C) 2004 Robert Love
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <getopt.h>
33 #include <sched.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/syscall.h>
38
39 struct bitmask {
40         unsigned int size;
41         unsigned long *maskp;
42 };
43
44 static void show_usage(const char *cmd)
45 {
46         fprintf(stderr, "taskset (%s)\n", PACKAGE_STRING);
47         fprintf(stderr, "usage: %s [options] [mask | cpu-list] [pid |"\
48                 " cmd [args...]]\n", cmd);
49         fprintf(stderr, "set or get the affinity of a process\n\n");
50         fprintf(stderr, "  -p, --pid                  "
51                 "operate on existing given pid\n");
52         fprintf(stderr, "  -c, --cpu-list             "\
53                 "display and specify cpus in list format\n");
54         fprintf(stderr, "  -h, --help                 display this help\n");
55         fprintf(stderr, "  -V, --version              "\
56                 "output version information\n\n");
57         fprintf(stderr, "The default behavior is to run a new command:\n");
58         fprintf(stderr, "  %s 03 sshd -b 1024\n", cmd);
59         fprintf(stderr, "You can retrieve the mask of an existing task:\n");
60         fprintf(stderr, "  %s -p 700\n", cmd);
61         fprintf(stderr, "Or set it:\n");
62         fprintf(stderr, "  %s -p 03 700\n", cmd);
63         fprintf(stderr, "List format uses a comma-separated list instead"\
64                         " of a mask:\n");
65         fprintf(stderr, "  %s -pc 0,3,7-11 700\n", cmd);
66         fprintf(stderr, "Ranges in list format can take a stride argument:\n");
67         fprintf(stderr, "  e.g. 0-31:2 is equivalent to mask 0x55555555\n\n");
68 }
69
70 static inline int val_to_char(int v)
71 {
72         if (v >= 0 && v < 10)
73                 return '0' + v;
74         else if (v >= 10 && v < 16)
75                 return ('a' - 10) + v;
76         else 
77                 return -1;
78 }
79
80 /*
81  * The following bitmask declarations, bitmask_*() routines, and associated
82  * _setbit() and _getbit() routines are:
83  * Copyright (c) 2004 Silicon Graphics, Inc. (SGI) All rights reserved.
84  * SGI publishes it under the terms of the GNU General Public License, v2,
85  * as published by the Free Software Foundation.
86  */
87 #define howmany(x,y) (((x)+((y)-1))/(y))
88 #define bitsperlong (8 * sizeof(unsigned long))
89 #define longsperbits(n) howmany(n, bitsperlong)
90 #define bytesperbits(x) ((x+7)/8)
91
92 static unsigned int _getbit(const struct bitmask *bmp, unsigned int n)
93 {
94         if (n < bmp->size)
95                 return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1;
96         else
97                 return 0;
98 }
99
100 static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v)
101 {
102         if (n < bmp->size) {
103                 if (v)
104                         bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong);
105                 else
106                         bmp->maskp[n/bitsperlong] &= ~(1UL << (n % bitsperlong));
107         }
108 }
109
110 int bitmask_isbitset(const struct bitmask *bmp, unsigned int i)
111 {
112         return _getbit(bmp, i);
113 }
114
115 struct bitmask *bitmask_clearall(struct bitmask *bmp)
116 {
117         unsigned int i;
118         for (i = 0; i < bmp->size; i++)
119                 _setbit(bmp, i, 0);
120         return bmp;
121 }
122
123 struct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i)
124 {
125         _setbit(bmp, i, 1);
126         return bmp;
127 }
128
129 unsigned int bitmask_nbytes(struct bitmask *bmp)
130 {
131         return longsperbits(bmp->size) * sizeof(unsigned long);
132 }
133
134 static char * cpuset_to_str(struct bitmask *mask, char *str)
135 {
136         int base;
137         char *ptr = str;
138         char *ret = 0;
139
140         for (base = mask->size - 4; base >= 0; base -= 4) {
141                 char val = 0;
142                 if (bitmask_isbitset(mask, base))
143                         val |= 1;
144                 if (bitmask_isbitset(mask, base + 1))
145                         val |= 2;
146                 if (bitmask_isbitset(mask, base + 2))
147                         val |= 4;
148                 if (bitmask_isbitset(mask, base + 3))
149                         val |= 8;
150                 if (!ret && val)
151                         ret = ptr;
152                 *ptr++ = val_to_char(val);
153         }
154         *ptr = 0;
155         return ret ? ret : ptr - 1;
156 }
157
158 struct bitmask *bitmask_alloc(unsigned int n)
159 {
160         struct bitmask *bmp;
161
162         bmp = malloc(sizeof(*bmp));
163         if (!bmp)
164                 return 0;
165         bmp->size = n;
166         bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long));
167         if (!bmp->maskp) {
168                 free(bmp);
169                 return 0;
170         }
171         return bmp;
172 }
173
174 static char * cpuset_to_cstr(struct bitmask *mask, char *str)
175 {
176         int i;
177         char *ptr = str;
178         int entry_made = 0;
179
180         for (i = 0; i < mask->size; i++) {
181                 if (bitmask_isbitset(mask, i)) {
182                         int j;
183                         int run = 0;
184                         entry_made = 1;
185                         for (j = i + 1; j < mask->size; j++) {
186                                 if (bitmask_isbitset(mask, j))
187                                         run++;
188                                 else
189                                         break;
190                         }
191                         if (!run)
192                                 sprintf(ptr, "%d,", i);
193                         else if (run == 1) {
194                                 sprintf(ptr, "%d,%d,", i, i + 1);
195                                 i++;
196                         } else {
197                                 sprintf(ptr, "%d-%d,", i, i + run);
198                                 i += run;
199                         }
200                         while (*ptr != 0)
201                                 ptr++;
202                 }
203         }
204         ptr -= entry_made;
205         *ptr = 0;
206
207         return str;
208 }
209
210 static inline int char_to_val(int c)
211 {
212         int cl;
213
214         cl = tolower(c);
215         if (c >= '0' && c <= '9')
216                 return c - '0';
217         else if (cl >= 'a' && cl <= 'f')
218                 return cl + (10 - 'a');
219         else
220                 return -1;
221 }
222
223 static int str_to_cpuset(struct bitmask *mask, const char* str)
224 {
225         int len = strlen(str);
226         const char *ptr = str + len - 1;
227         int base = 0;
228
229         /* skip 0x, it's all hex anyway */
230         if (len > 1 && !memcmp(str, "0x", 2L))
231                 str += 2;
232
233         bitmask_clearall(mask);
234         while (ptr >= str) {
235                 char val = char_to_val(*ptr);
236                 if (val == (char) -1)
237                         return -1;
238                 if (val & 1)
239                         bitmask_setbit(mask, base);
240                 if (val & 2)
241                         bitmask_setbit(mask, base + 1);
242                 if (val & 4)
243                         bitmask_setbit(mask, base + 2);
244                 if (val & 8)
245                         bitmask_setbit(mask, base + 3);
246                 len--;
247                 ptr--;
248                 base += 4;
249         }
250
251         return 0;
252 }
253
254 static const char *nexttoken(const char *q,  int sep)
255 {
256         if (q)
257                 q = strchr(q, sep);
258         if (q)
259                 q++;
260         return q;
261 }
262
263 static int cstr_to_cpuset(struct bitmask *mask, const char* str)
264 {
265         const char *p, *q;
266         q = str;
267         bitmask_clearall(mask);
268
269         while (p = q, q = nexttoken(q, ','), p) {
270                 unsigned int a; /* beginning of range */
271                 unsigned int b; /* end of range */
272                 unsigned int s; /* stride */
273                 const char *c1, *c2;
274
275                 if (sscanf(p, "%u", &a) < 1)
276                         return 1;
277                 b = a;
278                 s = 1;
279
280                 c1 = nexttoken(p, '-');
281                 c2 = nexttoken(p, ',');
282                 if (c1 != NULL && (c2 == NULL || c1 < c2)) {
283                         if (sscanf(c1, "%u", &b) < 1)
284                                 return 1;
285                         c1 = nexttoken(c1, ':');
286                         if (c1 != NULL && (c2 == NULL || c1 < c2))
287                                 if (sscanf(c1, "%u", &s) < 1) {
288                                         return 1;
289                         }
290                 }
291
292                 if (!(a <= b))
293                         return 1;
294                 while (a <= b) {
295                         bitmask_setbit(mask, a);
296                         a += s;
297                 }
298         }
299
300         return 0;
301 }
302
303 /*
304  * Number of bits in a CPU bitmask on current system
305  */
306 static int
307 max_number_of_cpus(void)
308 {
309         int n;
310         int cpus = 2048;
311
312         for (;;) {
313                 unsigned long buffer[longsperbits(cpus)];
314                 memset(buffer, 0, sizeof(buffer));
315                 /* the library version does not return size of cpumask_t */
316                 n = syscall(SYS_sched_getaffinity, 0, bytesperbits(cpus),
317                                                                 &buffer);
318                 if (n < 0 && errno == EINVAL && cpus < 1024*1024) {
319                         cpus *= 2;
320                         continue;
321                 }
322                 return n*8;
323         }
324         fprintf (stderr, "cannot determine NR_CPUS; aborting");
325         exit(1);
326         return 0;
327 }
328
329 int main(int argc, char *argv[])
330 {
331         struct bitmask *new_mask, *cur_mask;
332         pid_t pid = 0;
333         int opt, err;
334         char *mstr;
335         char *cstr;
336         int c_opt = 0;
337         unsigned int cpus_configured;
338         int new_mask_byte_size, cur_mask_byte_size;
339
340         struct option longopts[] = {
341                 { "pid",        0, NULL, 'p' },
342                 { "cpu-list",   0, NULL, 'c' },
343                 { "help",       0, NULL, 'h' },
344                 { "version",    0, NULL, 'V' },
345                 { NULL,         0, NULL, 0 }
346         };
347
348         while ((opt = getopt_long(argc, argv, "+pchV", longopts, NULL)) != -1) {
349                 int ret = 1;
350
351                 switch (opt) {
352                 case 'p':
353                         pid = atoi(argv[argc - 1]);
354                         break;
355                 case 'c':
356                         c_opt = 1;
357                         break;
358                 case 'V':
359                         printf("taskset (%s)\n", PACKAGE_STRING);
360                         return 0;
361                 case 'h':
362                         ret = 0;
363                 default:
364                         show_usage(argv[0]);
365                         return ret;
366                 }
367         }
368
369         if ((!pid && argc - optind < 2)
370                         || (pid && (argc - optind < 1 || argc - optind > 2))) {
371                 show_usage(argv[0]);
372                 return 1;
373         }
374
375         cpus_configured = max_number_of_cpus();
376
377         /*
378          * cur_mask is always used for the sched_getaffinity call
379          * On the sched_getaffinity the kernel demands a user mask of
380          * at least the size of its own cpumask_t.
381          */
382         cur_mask = bitmask_alloc(cpus_configured);
383         if (!cur_mask) {
384                 fprintf (stderr, "bitmask_alloc failed\n");
385                 exit(1);
386         }
387         cur_mask_byte_size = bitmask_nbytes(cur_mask);
388         mstr = malloc(1 + cur_mask->size / 4);
389         cstr = malloc(7 * cur_mask->size);
390
391         /*
392          * new_mask is always used for the sched_setaffinity call
393          * On the sched_setaffinity the kernel will zero-fill its
394          * cpumask_t if the user's mask is shorter.
395          */
396         new_mask = bitmask_alloc(cpus_configured);
397         if (!new_mask) {
398                 fprintf (stderr, "bitmask_alloc failed\n");
399                 exit(1);
400         }
401         new_mask_byte_size = bitmask_nbytes(new_mask);
402
403         if (pid) {
404                 if (sched_getaffinity(pid, cur_mask_byte_size,
405                                         (cpu_set_t *)cur_mask->maskp) < 0) {
406                         perror("sched_getaffinity");
407                         fprintf(stderr, "failed to get pid %d's affinity\n",
408                                 pid);
409                         return 1;
410                 }
411                 if (c_opt)
412                         printf("pid %d's current affinity list: %s\n", pid,
413                                 cpuset_to_cstr(cur_mask, cstr));
414                 else
415                         printf("pid %d's current affinity mask: %s\n", pid,
416                                 cpuset_to_str(cur_mask, mstr));
417
418                 if (argc - optind == 1)
419                         return 0;
420         }
421
422         if (c_opt)
423                 err = cstr_to_cpuset(new_mask, argv[optind]);
424         else
425                 err = str_to_cpuset(new_mask, argv[optind]);
426
427         if (err) {
428                 if (c_opt)
429                         fprintf(stderr, "failed to parse CPU list %s\n",
430                                 argv[optind]);
431                 else
432                         fprintf(stderr, "failed to parse CPU mask %s\n",
433                                 argv[optind]);
434                 return 1;
435         }
436
437         if (sched_setaffinity(pid, new_mask_byte_size,
438                                         (cpu_set_t *) new_mask->maskp) < 0) {
439                 perror("sched_setaffinity");
440                 fprintf(stderr, "failed to set pid %d's affinity.\n", pid);
441                 return 1;
442         }
443
444         if (sched_getaffinity(pid, cur_mask_byte_size,
445                                         (cpu_set_t *)cur_mask->maskp) < 0) {
446                 perror("sched_getaffinity");
447                 fprintf(stderr, "failed to get pid %d's affinity.\n", pid);
448                 return 1;
449         }
450
451         if (pid) {
452                 if (c_opt)
453                         printf("pid %d's new affinity list: %s\n", pid, 
454                                cpuset_to_cstr(cur_mask, cstr));
455                 else
456                         printf("pid %d's new affinity mask: %s\n", pid, 
457                                cpuset_to_str(cur_mask, mstr));
458         } else {
459                 argv += optind + 1;
460                 execvp(argv[0], argv);
461                 perror("execvp");
462                 fprintf(stderr, "failed to execute %s\n", argv[0]);
463                 return 1;
464         }
465
466         return 0;
467 }