blob: 3f32ec9b015fa364f698f6b6f0c4eec0cdf1d239 [file] [log] [blame]
Austin Schuh608514f2022-12-30 15:51:30 -08001#include <linux/securebits.h>
2#include <pwd.h>
3#include <sys/prctl.h>
4#include <sys/types.h>
5
6#include "aos/events/shm_event_loop.h"
7#include "aos/init.h"
8#include "aos/starter/irq_affinity_lib.h"
9#include "aos/starter/kthread_generated.h"
10#include "aos/util/top.h"
11#include "gflags/gflags.h"
12
13DEFINE_string(config, "aos_config.json", "File path of aos configuration");
14
15DEFINE_string(user, "",
16 "Starter runs as though this user ran a SUID binary if set.");
17
18namespace aos {
19
20cpu_set_t AffinityFromFlatbuffer(const flatbuffers::Vector<uint8_t> *v) {
21 cpu_set_t affinity;
22 CPU_ZERO(&affinity);
23 if (v == nullptr) {
24 for (int i = 0; i < CPU_SETSIZE; ++i) {
25 CPU_SET(i, &affinity);
26 }
27 } else {
28 for (uint8_t cpu : *v) {
29 CPU_SET(cpu, &affinity);
30 }
31 }
32 return affinity;
33}
34
35// Class to hold the configuration for an IRQ.
36struct ParsedIrqConfig {
37 std::string name;
38 cpu_set_t affinity;
39
40 void ConfigureIrq(int interrupt_number) const {
41 const std::string affinity_filename =
42 absl::StrCat("/proc/irq/", interrupt_number, "/smp_affinity");
43 const std::string contents = util::ReadFileToStringOrDie(affinity_filename);
44
45 std::string new_contents = std::string(contents.size() - 1, '0');
46
47 // Contents will be a padded string which is the size of the number of
48 // IRQs.
49 CHECK(!(CPU_SETSIZE & 0xf));
50 for (size_t i = 0; i < CPU_SETSIZE; i += 4) {
51 if (i / 4 >= new_contents.size()) {
52 break;
53 }
54 uint8_t byte = 0;
55 if (CPU_ISSET(i + 0, &affinity)) {
56 byte |= 1;
57 }
58 if (CPU_ISSET(i + 1, &affinity)) {
59 byte |= 2;
60 }
61 if (CPU_ISSET(i + 2, &affinity)) {
62 byte |= 4;
63 }
64 if (CPU_ISSET(i + 3, &affinity)) {
65 byte |= 8;
66 }
67 if (byte < 10) {
68 new_contents[new_contents.size() - 1 - i / 4] = '0' + byte;
69 } else {
70 new_contents[new_contents.size() - 1 - i / 4] = 'a' + (byte - 10);
71 }
72 }
73
74 if (contents != new_contents) {
75 util::WriteStringToFileOrDie(affinity_filename, new_contents);
76 }
77 }
78};
79
80// Class to hold the configuration for a kthread.
81struct ParsedKThreadConfig {
82 bool full_match = false;
83 std::string prefix;
84 std::string postfix;
85 starter::Scheduler scheduler;
86 int priority;
87 cpu_set_t affinity;
88
89 bool Matches(std::string_view candidate) const {
90 if (full_match) {
91 return candidate == prefix;
92 } else {
93 if (candidate.size() < prefix.size() + postfix.size()) {
94 return false;
95 }
96 if (candidate.substr(0, prefix.size()) != prefix) {
97 return false;
98 }
99 if (candidate.substr(candidate.size() - postfix.size(), postfix.size()) !=
100 postfix) {
101 return false;
102 }
103 return true;
104 }
105 }
106
107 void ConfigurePid(pid_t pid) const {
108 {
109 struct sched_param param;
110 param.sched_priority = priority;
111 int new_scheduler;
112 switch (scheduler) {
113 case starter::Scheduler::SCHEDULER_OTHER:
114 new_scheduler = SCHED_OTHER;
115 break;
116 case starter::Scheduler::SCHEDULER_RR:
117 new_scheduler = SCHED_RR;
118 break;
119 case starter::Scheduler::SCHEDULER_FIFO:
120 new_scheduler = SCHED_FIFO;
121 break;
122 default:
123 LOG(FATAL) << "Unknown scheduler";
124 }
125 PCHECK(sched_setscheduler(pid, new_scheduler, &param) == 0);
126 }
127 PCHECK(sched_setaffinity(pid, sizeof(affinity), &affinity) == 0);
128 }
129};
130
131// TODO(austin): Clean this up a bit, and maybe we can add some tests.
132class IrqAffinity {
133 public:
134 IrqAffinity(
135 EventLoop *event_loop,
136 const aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
137 &irq_affinity_config)
138 : top_(event_loop) {
139 if (irq_affinity_config.message().has_kthreads()) {
140 kthreads_.reserve(irq_affinity_config.message().kthreads()->size());
141 for (const starter::KthreadConfig *kthread_config :
142 *irq_affinity_config.message().kthreads()) {
143 LOG(INFO) << "Kthread " << aos::FlatbufferToJson(kthread_config);
144 CHECK(kthread_config->has_name()) << ": Name required";
145 const size_t star_position =
146 kthread_config->name()->string_view().find('*');
147 const bool has_star = star_position != std::string_view::npos;
148
149 kthreads_.push_back(ParsedKThreadConfig{
150 .full_match = !has_star,
151 .prefix = std::string(
152 !has_star ? kthread_config->name()->string_view()
153 : kthread_config->name()->string_view().substr(
154 0, star_position)),
155 .postfix = std::string(
156 !has_star ? ""
157 : kthread_config->name()->string_view().substr(
158 star_position + 1)),
159 .scheduler = kthread_config->scheduler(),
160 .priority = kthread_config->priority(),
161 .affinity = AffinityFromFlatbuffer(kthread_config->affinity()),
162 });
163 }
164 }
165
166 if (irq_affinity_config.message().has_irqs()) {
167 irqs_.reserve(irq_affinity_config.message().irqs()->size());
168 for (const starter::IrqConfig *irq_config :
169 *irq_affinity_config.message().irqs()) {
170 CHECK(irq_config->has_name()) << ": Name required";
171 LOG(INFO) << "IRQ " << aos::FlatbufferToJson(irq_config);
172 irqs_.push_back(ParsedIrqConfig{
173 .name = irq_config->name()->str(),
174 .affinity = AffinityFromFlatbuffer(irq_config->affinity()),
175 });
176 }
177 }
178
179 top_.set_track_top_processes(true);
180 top_.set_on_reading_update([this]() {
181 for (const std::pair<const pid_t, util::Top::ProcessReadings> &reading :
182 top_.readings()) {
183 if (reading.second.kthread) {
184 for (const ParsedKThreadConfig &match : kthreads_) {
185 if (match.Matches(reading.second.name)) {
186 match.ConfigurePid(reading.first);
187 break;
188 }
189 }
190 }
191 }
192
193 interrupts_status_.Update();
194
195 for (const InterruptsStatus::InterruptState &state :
196 interrupts_status_.states()) {
197 for (const ParsedIrqConfig &match : irqs_) {
198 bool matched = false;
199 for (const std::string &action : state.actions) {
200 if (match.name == action) {
201 matched = true;
202 break;
203 }
204 }
205 if (matched) {
206 match.ConfigureIrq(state.interrupt_number);
207 }
208 }
209 }
210 });
211 }
212
213 private:
214 util::Top top_;
215
216 // TODO(austin): Publish message with everything in it.
217 // TODO(austin): Make Top report out affinity + priority + scheduler for
218 // posterity.
219
220 std::vector<ParsedKThreadConfig> kthreads_;
221 std::vector<ParsedIrqConfig> irqs_;
222
223 InterruptsStatus interrupts_status_;
224};
225
226} // namespace aos
227
228int main(int argc, char **argv) {
229 aos::InitGoogle(&argc, &argv);
230
231 if (!FLAGS_user.empty()) {
232 // Maintain root permissions as we switch to become the user so we can
233 // actually manipulate priorities.
234 PCHECK(prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NOROOT) ==
235 0);
236
237 uid_t uid;
238 uid_t gid;
239 {
240 struct passwd *user_data = getpwnam(FLAGS_user.c_str());
241 if (user_data != nullptr) {
242 uid = user_data->pw_uid;
243 gid = user_data->pw_gid;
244 } else {
245 LOG(FATAL) << "Could not find user " << FLAGS_user;
246 return 1;
247 }
248 }
249 // Change the real and effective IDs to the user we're running as. The
250 // effective IDs mean files we access (like shared memory) will happen as
251 // that user. The real IDs allow child processes with an different effective
252 // ID to still participate in signal sending/receiving.
253 constexpr int kUnchanged = -1;
254 if (setresgid(/* ruid */ gid, /* euid */ gid,
255 /* suid */ kUnchanged) != 0) {
256 PLOG(FATAL) << "Failed to change GID to " << FLAGS_user;
257 }
258
259 if (setresuid(/* ruid */ uid, /* euid */ uid,
260 /* suid */ kUnchanged) != 0) {
261 PLOG(FATAL) << "Failed to change UID to " << FLAGS_user;
262 }
263 }
264
265 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
266 aos::configuration::ReadConfig(FLAGS_config);
267
268 // TODO(austin): File instead of hard-coded JSON.
269 aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
270 irq_affinity_config =
271 aos::JsonToFlatbuffer<aos::starter::IrqAffinityConfig>(
272 R"json({
273 "irqs": [
274 {
275 "name": "ttyS2",
276 "affinity": [1]
277 },
278 {
279 "name": "dw-mci",
280 "affinity": [1]
281 },
282 {
283 "name": "mmc1",
284 "affinity": [1]
285 },
286 {
287 "name": "rkisp1",
288 "affinity": [2]
289 },
290 {
291 "name": "ff3c0000.i2c",
292 "affinity": [2]
293 },
294 {
295 "name": "ff3d0000.i2c",
296 "affinity": [2]
297 },
298 {
Austin Schuh035247f2023-01-08 13:53:00 -0800299 "name": "ff6e0000.dma-controller",
300 "affinity": [0]
301 },
302 {
303 "name": "ff1d0000.spi",
304 "affinity": [0]
305 },
306 {
Austin Schuh608514f2022-12-30 15:51:30 -0800307 "name": "eth0",
Austin Schuh035247f2023-01-08 13:53:00 -0800308 "affinity": [1]
Austin Schuh608514f2022-12-30 15:51:30 -0800309 }
310 ],
311 "kthreads": [
312 {
313 "name": "irq/*-ff940000.hdmi",
314 "scheduler": "SCHEDULER_OTHER"
315 },
316 {
317 "name": "irq/*-rockchip_usb2phy",
318 "scheduler": "SCHEDULER_OTHER"
319 },
320 {
321 "name": "irq/*-mmc0",
322 "scheduler": "SCHEDULER_OTHER"
323 },
324 {
325 "name": "irq/*-mmc1",
326 "scheduler": "SCHEDULER_OTHER"
327 },
328 {
329 "name": "irq/*-fe320000.mmc cd",
330 "scheduler": "SCHEDULER_OTHER"
331 },
332 {
333 "name": "irq/*-vc4 crtc",
334 "scheduler": "SCHEDULER_OTHER"
335 },
336 {
337 "name": "irq/*-rkisp1",
338 "scheduler": "SCHEDULER_FIFO",
339 "priority": 60,
340 "affinity": [2]
341 },
342 {
343 "name": "irq/*-ff3c0000.i2c",
344 "scheduler": "SCHEDULER_FIFO",
345 "priority": 51,
346 "affinity": [2]
347 },
348 {
349 "name": "irq/*-adis16505",
350 "scheduler": "SCHEDULER_FIFO",
Austin Schuh035247f2023-01-08 13:53:00 -0800351 "priority": 59,
352 "affinity": [0]
353 },
354 {
355 "name": "irq/*-ff6e0000.dma-controller",
356 "scheduler": "SCHEDULER_FIFO",
357 "priority": 59,
Austin Schuh608514f2022-12-30 15:51:30 -0800358 "affinity": [0]
359 },
360 {
361 "name": "spi0",
362 "scheduler": "SCHEDULER_FIFO",
363 "priority": 57,
364 "affinity": [0]
365 },
366 {
367 "name": "irq/*-eth0",
368 "scheduler": "SCHEDULER_FIFO",
369 "priority": 10,
370 "affinity": [1]
371 },
372 {
373 "name": "irq/*-rockchip_thermal",
374 "scheduler": "SCHEDULER_FIFO",
375 "priority": 1
376 }
377 ]
378})json");
379
380 aos::ShmEventLoop shm_event_loop(&config.message());
381
382 aos::IrqAffinity irq_affinity(&shm_event_loop, irq_affinity_config);
383
384 shm_event_loop.Run();
385
386 return 0;
387}