]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "numa.h" | |
5 | ||
6 | #include <cstring> | |
7 | #include <errno.h> | |
8 | #include <iostream> | |
9 | ||
10 | #include "include/stringify.h" | |
11 | #include "common/safe_io.h" | |
12 | ||
f67539c2 TL |
13 | using namespace std::literals; |
14 | ||
15 | using std::set; | |
16 | ||
11fdf7f2 TL |
17 | |
18 | // list | |
19 | #if defined(__linux__) | |
20 | int parse_cpu_set_list(const char *s, | |
21 | size_t *cpu_set_size, | |
22 | cpu_set_t *cpu_set) | |
23 | { | |
24 | CPU_ZERO(cpu_set); | |
25 | while (*s) { | |
26 | char *end; | |
27 | int a = strtol(s, &end, 10); | |
28 | if (end == s) { | |
29 | return -EINVAL; | |
30 | } | |
31 | if (*end == '-') { | |
32 | s = end + 1; | |
33 | int b = strtol(s, &end, 10); | |
34 | if (end == s) { | |
35 | return -EINVAL; | |
36 | } | |
37 | for (; a <= b; ++a) { | |
38 | CPU_SET(a, cpu_set); | |
39 | } | |
40 | *cpu_set_size = a; | |
41 | } else { | |
42 | CPU_SET(a, cpu_set); | |
43 | *cpu_set_size = a + 1; | |
44 | } | |
45 | if (*end == 0) { | |
46 | break; | |
47 | } | |
48 | if (*end != ',') { | |
49 | return -EINVAL; | |
50 | } | |
51 | s = end + 1; | |
52 | } | |
53 | return 0; | |
54 | } | |
55 | ||
56 | std::string cpu_set_to_str_list(size_t cpu_set_size, | |
57 | const cpu_set_t *cpu_set) | |
58 | { | |
59 | std::string r; | |
60 | unsigned a = 0; | |
61 | while (true) { | |
62 | while (a < cpu_set_size && !CPU_ISSET(a, cpu_set)) { | |
63 | ++a; | |
64 | } | |
65 | if (a >= cpu_set_size) { | |
66 | break; | |
67 | } | |
68 | unsigned b = a + 1; | |
69 | while (b < cpu_set_size && CPU_ISSET(b, cpu_set)) { | |
70 | ++b; | |
71 | } | |
72 | if (r.size()) { | |
73 | r += ","; | |
74 | } | |
75 | if (b > a + 1) { | |
76 | r += stringify(a) + "-" + stringify(b - 1); | |
77 | } else { | |
78 | r += stringify(a); | |
79 | } | |
80 | a = b; | |
81 | } | |
82 | return r; | |
83 | } | |
84 | ||
85 | std::set<int> cpu_set_to_set(size_t cpu_set_size, | |
86 | const cpu_set_t *cpu_set) | |
87 | { | |
88 | set<int> r; | |
89 | unsigned a = 0; | |
90 | while (true) { | |
91 | while (a < cpu_set_size && !CPU_ISSET(a, cpu_set)) { | |
92 | ++a; | |
93 | } | |
94 | if (a >= cpu_set_size) { | |
95 | break; | |
96 | } | |
97 | unsigned b = a + 1; | |
98 | while (b < cpu_set_size && CPU_ISSET(b, cpu_set)) { | |
99 | ++b; | |
100 | } | |
101 | while (a < b) { | |
102 | r.insert(a); | |
103 | ++a; | |
104 | } | |
105 | } | |
106 | return r; | |
107 | } | |
108 | ||
109 | ||
110 | int get_numa_node_cpu_set( | |
111 | int node, | |
112 | size_t *cpu_set_size, | |
113 | cpu_set_t *cpu_set) | |
114 | { | |
115 | std::string fn = "/sys/devices/system/node/node"; | |
116 | fn += stringify(node); | |
117 | fn += "/cpulist"; | |
118 | int fd = ::open(fn.c_str(), O_RDONLY); | |
119 | if (fd < 0) { | |
120 | return -errno; | |
121 | } | |
122 | char buf[1024]; | |
123 | int r = safe_read(fd, &buf, sizeof(buf)); | |
124 | if (r < 0) { | |
125 | goto out; | |
126 | } | |
127 | buf[r] = 0; | |
128 | while (r > 0 && ::isspace(buf[--r])) { | |
129 | buf[r] = 0; | |
130 | } | |
131 | r = parse_cpu_set_list(buf, cpu_set_size, cpu_set); | |
132 | if (r < 0) { | |
133 | goto out; | |
134 | } | |
135 | r = 0; | |
136 | out: | |
137 | ::close(fd); | |
138 | return r; | |
139 | } | |
140 | ||
92f5a8d4 TL |
141 | static int easy_readdir(const std::string& dir, std::set<std::string> *out) |
142 | { | |
143 | DIR *h = ::opendir(dir.c_str()); | |
144 | if (!h) { | |
145 | return -errno; | |
146 | } | |
147 | struct dirent *de = nullptr; | |
148 | while ((de = ::readdir(h))) { | |
149 | if (strcmp(de->d_name, ".") == 0 || | |
150 | strcmp(de->d_name, "..") == 0) { | |
151 | continue; | |
152 | } | |
153 | out->insert(de->d_name); | |
154 | } | |
155 | closedir(h); | |
156 | return 0; | |
157 | } | |
158 | ||
159 | int set_cpu_affinity_all_threads(size_t cpu_set_size, cpu_set_t *cpu_set) | |
160 | { | |
161 | // first set my affinity | |
162 | int r = sched_setaffinity(getpid(), cpu_set_size, cpu_set); | |
163 | if (r < 0) { | |
164 | return -errno; | |
165 | } | |
166 | ||
167 | // make 2 passes here so that we (hopefully) catch racing threads creating | |
168 | // threads. | |
169 | for (unsigned pass = 0; pass < 2; ++pass) { | |
170 | // enumerate all child threads from /proc | |
171 | std::set<std::string> ls; | |
172 | std::string path = "/proc/"s + stringify(getpid()) + "/task"; | |
173 | r = easy_readdir(path, &ls); | |
174 | if (r < 0) { | |
175 | return r; | |
176 | } | |
177 | for (auto& i : ls) { | |
178 | pid_t tid = atoll(i.c_str()); | |
179 | if (!tid) { | |
180 | continue; // wtf | |
181 | } | |
182 | r = sched_setaffinity(tid, cpu_set_size, cpu_set); | |
183 | if (r < 0) { | |
184 | return -errno; | |
185 | } | |
186 | } | |
187 | } | |
188 | return 0; | |
189 | } | |
190 | ||
f67539c2 | 191 | #else |
11fdf7f2 TL |
192 | int parse_cpu_set_list(const char *s, |
193 | size_t *cpu_set_size, | |
194 | cpu_set_t *cpu_set) | |
195 | { | |
196 | return -ENOTSUP; | |
197 | } | |
198 | ||
199 | std::string cpu_set_to_str_list(size_t cpu_set_size, | |
200 | const cpu_set_t *cpu_set) | |
201 | { | |
202 | return {}; | |
203 | } | |
204 | ||
205 | std::set<int> cpu_set_to_set(size_t cpu_set_size, | |
206 | const cpu_set_t *cpu_set) | |
207 | { | |
208 | return {}; | |
209 | } | |
210 | ||
211 | int get_numa_node_cpu_set(int node, | |
212 | size_t *cpu_set_size, | |
213 | cpu_set_t *cpu_set) | |
214 | { | |
215 | return -ENOTSUP; | |
216 | } | |
217 | ||
92f5a8d4 TL |
218 | int set_cpu_affinity_all_threads(size_t cpu_set_size, |
219 | cpu_set_t *cpu_set) | |
220 | { | |
221 | return -ENOTSUP; | |
222 | } | |
223 | ||
11fdf7f2 | 224 | #endif |