aboutsummaryrefslogtreecommitdiff
path: root/toys/taskset.c
blob: 7ac3b572cfbd11cfa1dfd4003d009aa4a7b938fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* vi: set sw=4 ts=4:
 *
 * taskset.c - Retrieve or set the CPU affinity of a process.
 *
 * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
 *
 * No standard.

USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_BIN|TOYFLAG_STAYROOT))

config TASKSET
	bool "taskset"
	default y
	help
	  usage: taskset [-ap] [mask] [PID | cmd [args...]]

	  Launch a new task which may only run on certain processors, or change
	  the processor affinity of an exisitng PID.

	  Mask is a hex string where each bit represents a processor the process
	  is allowed to run on. PID without a mask displays existing affinity.

	  -p	Set/get the affinity of given PID instead of a new command.
	  -a	Set/get the affinity of all threads of the PID.
*/

#include "toys.h"

#define FLAG_a 0x1
#define FLAG_p 0x2

// Prototype for syscall wrappers sched.h refuses to give us
int sched_setaffinity(pid_t pid, size_t size, void *cpuset);
int sched_getaffinity(pid_t pid, size_t size, void *cpuset);

// mask is an array of long, which makes the layout a bit weird on big
// endian systems but as long as it's consistent...

static void do_taskset(pid_t pid, int quiet)
{
	unsigned long *mask = (unsigned long *)toybuf;
	char *s = *toys.optargs, *failed = "failed to %s %d's affinity";
	int i, j, k;

	for (i=0; ; i++) {
		if (!quiet) {
			int j = sizeof(toybuf), flag = 0;

			if (sched_getaffinity(pid, sizeof(toybuf), (void *)mask))
				perror_exit(failed, "get", pid);

			printf("pid %d's %s affinity mask: ", pid, i ? "new" : "current");

			while (j--) {
				int x = 255 & (mask[j/sizeof(long)] >> (8*(j&(sizeof(long)-1))));

				if (flag) printf("%02x", x);
				else if (x) {
					flag++;
					printf("%x", x);
				}
			}
			putchar('\n');
		}

		if (i || toys.optc < 2) return;

		memset(toybuf, 0, sizeof(toybuf));
		k = strlen(s = *toys.optargs);
		s += k;
		for (j = 0; j<k; j++) {
			unsigned long digit = *(--s) - '0';

			if (digit > 9) digit = 10 + tolower(*s)-'a';
			if (digit > 15) error_exit("bad mask '%s'", *toys.optargs);
			mask[j/(2*sizeof(long))] |= digit << 4*(j&((2*sizeof(long))-1));
		}

		if (sched_setaffinity(pid, sizeof(toybuf), (void *)mask))
			perror_exit(failed, "set", pid);
	}
}

static int task_callback(struct dirtree *new)
{
	if (!new->parent) return DIRTREE_RECURSE;
	if (isdigit(*new->name)) do_taskset(atoi(new->name), 0);

	return 0;
}

void taskset_main(void)
{
	if (!(toys.optflags & FLAG_p)) {
		if (toys.optc < 2) error_exit("Needs 2 args");
		do_taskset(getpid(), 1);
		xexec(toys.optargs+1);
	} else {
		char *c;
		pid_t pid = strtol(toys.optargs[toys.optc-1], &c, 10);

		if (*c) error_exit("Not int %s", toys.optargs[1]);

		if (toys.optflags & FLAG_a) {
			char buf[33];
			sprintf(buf, "/proc/%ld/task/", (long)pid);
			dirtree_read(buf, task_callback);
		} else do_taskset(pid, 0);
	}
}