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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h> /* for cpu sets */
#include <unistd.h>
#include "migration.h"
extern ssize_t read_file(const char* fname, void* buf, size_t maxlen);
int release_master()
{
static const char NO_CPU[] = "NO_CPU";
char buf[7] = {0}; /* up to 999999 CPUs */
int master = -1;
int ret = read_file("/proc/litmus/release_master", &buf, sizeof(buf)-1);
if ((ret > 0) && (strncmp(buf, NO_CPU, sizeof(NO_CPU)-1) != 0))
master = atoi(buf);
return master;
}
int num_online_cpus()
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
void set_mapping(char* buf, int len, cpu_set_t** set, size_t *sz)
{
int nbits;
char *chunk_str;
int i;
/* init vals returned to callee */
*set = NULL;
*sz = 0;
nbits = 32*(len/9) + 4*(len%9); /* compute bits, accounting for commas */
*set = CPU_ALLOC(nbits);
*sz = CPU_ALLOC_SIZE(nbits);
CPU_ZERO_S(*sz, *set);
/* process LSB chunks first (at the end of the str) and move backward */
chunk_str = buf + len;
i = 0;
do {
unsigned long chunk;
/* since strtoul stops processing the string with occurrence of
first non-digit character, it is necessary to read 8-bytes
on first iteration for ignoring the leading comma*/
chunk_str -= (9 + ((i == 0) ? -1 : 0));
if (chunk_str < buf)
chunk_str = buf; /* when MSB mask is less than 8 chars */
chunk = strtoul(chunk_str, NULL, 16);
while (chunk) {
int j = ffsl(chunk) - 1;
int x = i*32 + j;
CPU_SET_S(x, *sz, *set);
chunk &= ~(1ul << j);
}
i += 1;
} while (chunk_str > buf);
}
static int read_mapping(int idx, const char* which, cpu_set_t** set, size_t *sz)
{
/* Max CPUs = 4096 */
int ret = -1;
char buf[4096/4 /* enough chars for hex data (4 CPUs per char) */
+ 4096/(4*8) /* for commas (separate groups of 8 chars) */
+ 1] = {0}; /* for \0 */
char fname[80] = {0};
int len;
if (num_online_cpus() > 4096)
goto out;
/* Read string is in the format of <mask>[,<mask>]*. All <mask>s following
a comma are 8 chars (representing a 32-bit mask). The first <mask> may
have fewer chars. Bits are MSB to LSB, left to right. */
snprintf(fname, sizeof(fname), "/proc/litmus/%s/%d", which, idx);
ret = read_file(fname, &buf, sizeof(buf)-1);
if (ret <= 0)
goto out;
len = strnlen(buf, sizeof(buf));
/* if there is, omit newline at the end of string */
if (buf[len-1] == '\n') {
buf[len-1] = '\0';
len -= 1;
}
set_mapping(buf, len, set, sz);
ret = 0;
out:
return ret;
}
static unsigned long long int cpusettoull(cpu_set_t* bits, size_t sz)
{
unsigned long long mask = 0;
int i;
for (i = 0; i < sizeof(mask)*8; ++i) {
if (CPU_ISSET_S(i, sz, bits)) {
mask |= (1ull) << i;
}
}
return mask;
}
int domain_to_cpus(int domain, unsigned long long int* mask)
{
/* TODO: Support more than 64 CPUs. Instead of using 'ull' for 'mask',
consider using gcc's __uint128_t or some struct. */
cpu_set_t *bits;
size_t sz;
int ret;
/* number of CPUs exceeds what we can pack in ull */
if (num_online_cpus() > sizeof(unsigned long long int)*8)
return -1;
ret = read_mapping(domain, "domains", &bits, &sz);
if (!ret) {
*mask = cpusettoull(bits, sz);
CPU_FREE(bits);
}
return ret;
}
int cpu_to_domains(int cpu, unsigned long long int* mask)
{
/* TODO: Support more than 64 domains. Instead of using 'ull' for 'mask',
consider using gcc's __uint128_t or some struct. */
cpu_set_t *bits;
size_t sz;
int ret;
/* number of CPUs exceeds what we can pack in ull */
if (num_online_cpus() > sizeof(unsigned long long int)*8)
return -1;
ret = read_mapping(cpu, "cpus", &bits, &sz);
if (!ret) {
*mask = cpusettoull(bits, sz);
CPU_FREE(bits);
}
return ret;
}
int domain_to_first_cpu(int domain)
{
cpu_set_t *bits;
size_t sz;
int i, n_online;
int first;
int ret = read_mapping(domain, "domains", &bits, &sz);
if (ret)
return ret;
n_online = num_online_cpus();
first = -1; /* assume failure */
for (i = 0; i < n_online; ++i) {
if(CPU_ISSET_S(i, sz, bits)) {
first = i;
break;
}
}
CPU_FREE(bits);
return first;
}
int be_migrate_thread_to_cpu(pid_t tid, int target_cpu)
{
cpu_set_t *cpu_set;
size_t sz;
int num_cpus;
int ret;
/* TODO: Error check to make sure that tid is not a real-time task. */
if (target_cpu < 0)
return -1;
num_cpus = num_online_cpus();
if (num_cpus == -1)
return -1;
if (target_cpu >= num_cpus)
return -1;
cpu_set = CPU_ALLOC(num_cpus);
sz = CPU_ALLOC_SIZE(num_cpus);
CPU_ZERO_S(sz, cpu_set);
CPU_SET_S(target_cpu, sz, cpu_set);
/* apply to caller */
if (tid == 0)
tid = gettid();
ret = sched_setaffinity(tid, sz, cpu_set);
CPU_FREE(cpu_set);
return ret;
}
int be_migrate_thread_to_domain(pid_t tid, int domain)
{
int ret;
cpu_set_t *cpu_set;
size_t sz;
ret = read_mapping(domain, "domains", &cpu_set, &sz);
if (ret != 0)
return ret;
/* apply to caller */
if (tid == 0)
tid = gettid();
ret = sched_setaffinity(tid, sz, cpu_set);
CPU_FREE(cpu_set);
return ret;
}
int be_migrate_to_cpu(int target_cpu)
{
return be_migrate_thread_to_cpu(0, target_cpu);
}
int be_migrate_to_domain(int domain)
{
return be_migrate_thread_to_domain(0, domain);
}
/* deprecated functions. */
int be_migrate_to_cluster(int cluster, int cluster_size)
{
return be_migrate_to_domain(cluster);
}
int cluster_to_first_cpu(int cluster, int cluster_size)
{
return domain_to_first_cpu(cluster);
}
int partition_to_cpu(int partition)
{
return domain_to_first_cpu(partition);
}
|